├── emoji-site ├── go.mod ├── Dockerfile ├── main.go └── static │ └── index.html ├── .gitignore ├── resources ├── menu_icon.png ├── solutions.dat └── acceptable.dat ├── store-assets ├── banner.png ├── wordle-144.png └── wordle-48.png ├── src └── c │ ├── help_window.h │ ├── stat_window.h │ ├── notify_layer.h │ ├── title_layer.h │ ├── cursor_layer.h │ ├── share_layer.h │ ├── distribution_layer.h │ ├── letter_layer.h │ ├── game.h │ ├── model.h │ ├── word_layer.h │ ├── stat_tracker.h │ ├── cursor_layer.c │ ├── notify_layer.c │ ├── title_layer.c │ ├── stat_tracker.c │ ├── word_layer.c │ ├── distribution_layer.c │ ├── share_layer.c │ ├── main.c │ ├── model.c │ ├── help_window.c │ ├── letter_layer.c │ ├── stat_window.c │ ├── game.c │ └── vendor │ ├── qrcodegen.h │ └── qrcodegen.c ├── LICENSE ├── package.json ├── README.md └── wscript /emoji-site/go.mod: -------------------------------------------------------------------------------- 1 | module emoji-site 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .lock-waf* 2 | shell.nix 3 | build/ 4 | .DS_Store 5 | node_modules/ 6 | -------------------------------------------------------------------------------- /resources/menu_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Katharine/pebble-wordle/HEAD/resources/menu_icon.png -------------------------------------------------------------------------------- /store-assets/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Katharine/pebble-wordle/HEAD/store-assets/banner.png -------------------------------------------------------------------------------- /store-assets/wordle-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Katharine/pebble-wordle/HEAD/store-assets/wordle-144.png -------------------------------------------------------------------------------- /store-assets/wordle-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Katharine/pebble-wordle/HEAD/store-assets/wordle-48.png -------------------------------------------------------------------------------- /src/c/help_window.h: -------------------------------------------------------------------------------- 1 | #ifndef HELP_WINDOW_H 2 | #define HELP_WINDOW_H 3 | 4 | void help_window_push(); 5 | 6 | #endif -------------------------------------------------------------------------------- /src/c/stat_window.h: -------------------------------------------------------------------------------- 1 | #ifndef STAT_WINDOW_H 2 | #define STAT_WINDOW_H 3 | 4 | void stat_window_push(); 5 | void stat_window_pop(); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /src/c/notify_layer.h: -------------------------------------------------------------------------------- 1 | #ifndef NOTIFY_LAYER_H 2 | #define NOTIFY_LAYER_H 3 | 4 | typedef Layer NotifyLayer; 5 | 6 | NotifyLayer *notify_layer_create(char *message); 7 | void notify_layer_destroy(NotifyLayer *layer); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/c/title_layer.h: -------------------------------------------------------------------------------- 1 | #ifndef TITLE_LAYER_H 2 | #define TITLE_LAYER_H 3 | 4 | typedef Layer TitleLayer; 5 | 6 | TitleLayer *title_layer_create(GPoint where, bool animated); 7 | void title_layer_destroy(TitleLayer *layer); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/c/cursor_layer.h: -------------------------------------------------------------------------------- 1 | #ifndef CURSOR_LAYER_H 2 | #define CURSOR_LAYER_H 3 | 4 | #include 5 | 6 | typedef Layer CursorLayer; 7 | 8 | CursorLayer *cursor_layer_create(GPoint where); 9 | void cursor_layer_destroy(CursorLayer *layer); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /emoji-site/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.16.3 as build 2 | 3 | WORKDIR /go/src/app 4 | 5 | ADD . /go/src/app 6 | RUN CGO_ENABLED=0 GOOS=linux go build -o /go/bin/app 7 | 8 | # This app is so simple it can happily run in an empty filesystem. 9 | FROM scratch 10 | COPY --from=build /go/bin/app / 11 | ENTRYPOINT ["/app"] 12 | -------------------------------------------------------------------------------- /src/c/share_layer.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "model.h" 3 | #include "game.h" 4 | 5 | typedef Layer ShareLayer; 6 | 7 | ShareLayer *share_layer_create(GRect frame); 8 | void share_layer_destroy(ShareLayer *layer); 9 | void share_layer_set_game_state(ShareLayer *layer, int wordle_num, LetterStatus guesses[GUESS_LIMIT][WORD_LENGTH]); 10 | -------------------------------------------------------------------------------- /src/c/distribution_layer.h: -------------------------------------------------------------------------------- 1 | #ifndef DISTRIBUTION_LAYER_H 2 | #define DISTRIBUTION_LAYER_H 3 | 4 | #include 5 | #include "stat_tracker.h" 6 | 7 | typedef Layer DistributionLayer; 8 | 9 | DistributionLayer *distribution_layer_create(GRect frame, StatTracker *stat_tracker); 10 | void distribution_layer_destroy(DistributionLayer *layer); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/c/letter_layer.h: -------------------------------------------------------------------------------- 1 | #ifndef LETTER_LAYER_H 2 | #define LETTER_LAYER_H 3 | 4 | #include 5 | #include "model.h" 6 | 7 | #define LETTER_LAYER_SIZE 23 8 | 9 | typedef Layer LetterLayer; 10 | 11 | LetterLayer *letter_layer_create(GPoint where); 12 | void letter_layer_destroy(LetterLayer *layer); 13 | void letter_layer_set_letter(LetterLayer *layer, char letter, LetterStatus status, bool confirmed, int animated); 14 | 15 | #endif -------------------------------------------------------------------------------- /src/c/game.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_H 2 | #define GAME_H 3 | #include "model.h" 4 | 5 | #define GUESS_LIMIT 6 6 | 7 | enum GameStatus { 8 | GameStatusPlaying, 9 | GameStatusWon, 10 | GameStatusLost, 11 | }; 12 | typedef enum GameStatus GameStatus; 13 | 14 | void game_restore(); 15 | void show_game(); 16 | int game_get_number(); 17 | GameStatus game_get_status(); 18 | int game_get_guesses(LetterStatus guesses[GUESS_LIMIT][WORD_LENGTH]); 19 | int game_get_guess_number(); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/c/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | 4 | #define WORD_LENGTH 5 5 | 6 | enum LetterStatus { 7 | LetterStatusNeutral, 8 | LetterStatusNotPresent, 9 | LetterStatusWrongPosition, 10 | LetterStatusCorrect, 11 | LetterStatusCount, 12 | }; 13 | typedef enum LetterStatus LetterStatus; 14 | 15 | bool is_valid_word(char word[WORD_LENGTH]); 16 | bool score_word(char word[WORD_LENGTH], char expected[WORD_LENGTH], LetterStatus result[WORD_LENGTH]); 17 | void word_of_the_day(char word[WORD_LENGTH]); 18 | int wordle_number(); 19 | 20 | #endif -------------------------------------------------------------------------------- /src/c/word_layer.h: -------------------------------------------------------------------------------- 1 | #ifndef WORD_LAYER_H 2 | #define WORD_LAYER_H 3 | 4 | #include 5 | #include "model.h" 6 | #include "letter_layer.h" 7 | 8 | #define WORD_LAYER_HEIGHT LETTER_LAYER_SIZE + 2 9 | 10 | typedef Layer WordLayer; 11 | 12 | WordLayer *word_layer_create(GPoint where); 13 | void word_layer_destroy(WordLayer *layer); 14 | void word_layer_set_letter(WordLayer *layer, int index, char letter, LetterStatus status, bool confirmed); 15 | void word_layer_set_cursor(WordLayer *layer, int index); 16 | void word_layer_apply_score(WordLayer *layer, LetterStatus statuses[WORD_LENGTH], bool animated); 17 | 18 | #endif -------------------------------------------------------------------------------- /src/c/stat_tracker.h: -------------------------------------------------------------------------------- 1 | #ifndef STAT_TRACKER_H 2 | #define STAT_TRACKER_H 3 | 4 | struct StatTracker; 5 | typedef struct StatTracker StatTracker; 6 | 7 | void stat_tracker_record_result(StatTracker *tracker, int wordle_num, int result); 8 | StatTracker *stat_tracker_load(); 9 | void stat_tracker_destroy(StatTracker *tracker); 10 | int stat_tracker_get_current_streak(StatTracker *tracker); 11 | int stat_tracker_get_max_streak(StatTracker *tracker); 12 | int stat_tracker_get_total_played(StatTracker *tracker); 13 | int stat_tracker_get_win_percent(StatTracker *tracker); 14 | int stat_tracker_get_distribution_bucket(StatTracker *tracker, int bucket); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/c/cursor_layer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cursor_layer.h" 3 | #include "letter_layer.h" 4 | 5 | static void prv_update_proc(Layer *layer, GContext *ctx); 6 | 7 | CursorLayer *cursor_layer_create(GPoint where) { 8 | Layer *layer = layer_create(GRect(where.x, where.y, LETTER_LAYER_SIZE + 2, LETTER_LAYER_SIZE + 2)); 9 | layer_set_update_proc(layer, prv_update_proc); 10 | return layer; 11 | } 12 | 13 | void cursor_layer_destroy(CursorLayer *layer) { 14 | layer_destroy(layer); 15 | } 16 | 17 | static void prv_update_proc(Layer *layer, GContext *ctx) { 18 | graphics_context_set_stroke_color(ctx, GColorBlack); 19 | graphics_draw_rect(ctx, layer_get_bounds(layer)); 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Katharine Berry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pebble-wordle", 3 | "author": "Katharine Berry", 4 | "version": "1.2.0", 5 | "keywords": [ 6 | "pebble-app" 7 | ], 8 | "private": true, 9 | "dependencies": { 10 | "pebble-events": "^1.2.0" 11 | }, 12 | "pebble": { 13 | "displayName": "Pebble Wordle", 14 | "uuid": "f63e63cf-445b-48f6-8922-2d785981443d", 15 | "sdkVersion": "3", 16 | "enableMultiJS": true, 17 | "targetPlatforms": [ 18 | "aplite", 19 | "basalt", 20 | "diorite" 21 | ], 22 | "watchapp": { 23 | "watchface": false 24 | }, 25 | "messageKeys": [ 26 | "dummy" 27 | ], 28 | "resources": { 29 | "media": [ 30 | { 31 | "type": "raw", 32 | "name": "ACCEPTABLE_WORDS", 33 | "file": "acceptable.dat" 34 | }, 35 | { 36 | "type": "raw", 37 | "name": "SOLUTION_WORDS", 38 | "file": "solutions.dat" 39 | }, 40 | { 41 | "type": "bitmap", 42 | "name": "MENU_ICON", 43 | "menuIcon": true, 44 | "file": "menu_icon.png" 45 | } 46 | ] 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /emoji-site/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "embed" 5 | "flag" 6 | "io" 7 | "log" 8 | "net/http" 9 | "time" 10 | ) 11 | 12 | var listenAddr = flag.String("listen", "0.0.0.0:8080", "port to listen on") 13 | 14 | // This embeds the entirety of the static directory in the binary. 15 | //go:embed static 16 | var staticContent embed.FS 17 | 18 | 19 | func handleGenericRequest(rw http.ResponseWriter, r *http.Request) { 20 | // Actually serve the file. 21 | // We can't use http.ServeFile because there are no real files - our static content is embedded in the binary. 22 | f, err := staticContent.Open("static/index.html") 23 | if err != nil { 24 | http.NotFound(rw, r) 25 | return 26 | } 27 | defer f.Close() 28 | rw.Header().Add("Cache-Control", "public, max-age=86400") 29 | http.ServeContent(rw, r, r.URL.Path, time.Time{}, f.(io.ReadSeeker)) 30 | } 31 | 32 | func main() { 33 | flag.Parse() 34 | sm := http.NewServeMux() 35 | sm.HandleFunc("/", handleGenericRequest) 36 | sm.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { 37 | _, _ = w.Write([]byte("rebble-emoji")) 38 | }) 39 | 40 | if err := http.ListenAndServe(*listenAddr, sm); err != nil { 41 | log.Fatalf("Failed to listen and serve: %v.\n", err) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pebble-wordle 2 | 3 | Imagine wordle, but on your Pebble. That's pretty much it. 4 | 5 | Supports playing the game, stat tracking, and sharing your score. Runs 6 | 100% on-watch for the best experience. 7 | 8 | Works best on Pebble Time and Pebble Time Steel (basalt). Works on Pebble 2 (diorite), 9 | but the colour scheme is confusing. Basically works on the original Pebble and Pebble 10 | Steel, but the colour scheme is confusing and the sharing QR codes aren't available. 11 | 12 | ![](https://assets2.rebble.io/exact/144x168/623146422a976b000a6e73e8) 13 | ![](https://assets2.rebble.io/exact/144x168/6231463ab442c1000a1ff260) 14 | ![](https://assets2.rebble.io/exact/144x168/62314647b442c1000a1ff261) 15 | ![](https://assets2.rebble.io/exact/144x168/6231464c2a976b000a6e73e9) 16 | ![](https://assets2.rebble.io/exact/144x168/62314650b442c1000a1ff262) 17 | 18 | ## Word lists 19 | 20 | The word lists are stored in `resources/acceptable.dat` and `resources/solutions.dat`. 21 | `acceptable.dat` contains all possible words, including the ones in `solutions.dat`. 22 | `acceptable.dat` is in alphabetical order for efficient searching; breaking that 23 | ordering will break the valid word check. 24 | 25 | `solutions.dat` is the list of every solution word, in the order they will appear. 26 | -------------------------------------------------------------------------------- /src/c/notify_layer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "notify_layer.h" 3 | 4 | 5 | typedef struct { 6 | char *message; 7 | } NotifyLayerData; 8 | 9 | static GFont s_font; 10 | static void prv_update_proc(Layer *layer, GContext *ctx); 11 | 12 | NotifyLayer *notify_layer_create(char *message) { 13 | if (s_font == NULL) { 14 | s_font = fonts_get_system_font(FONT_KEY_GOTHIC_24); 15 | } 16 | GSize size = graphics_text_layout_get_content_size(message, s_font, GRect(0, 0, 104, 148), GTextOverflowModeWordWrap, GTextAlignmentCenter); 17 | size.w += 10; 18 | size.h += 10; 19 | Layer *layer = layer_create_with_data(GRect((144 - size.w) / 2, (168 - size.h) / 2, size.w, size.h), sizeof(NotifyLayerData)); 20 | layer_set_update_proc(layer, prv_update_proc); 21 | NotifyLayerData *data = layer_get_data(layer); 22 | data->message = malloc(strlen(message) + 1); 23 | strcpy(data->message, message); 24 | return layer; 25 | } 26 | 27 | void notify_layer_destroy(NotifyLayer *layer) { 28 | NotifyLayerData *data = layer_get_data(layer); 29 | free(data->message); 30 | layer_destroy(layer); 31 | } 32 | 33 | static void prv_update_proc(Layer *layer, GContext *ctx) { 34 | NotifyLayerData *data = layer_get_data(layer); 35 | GRect bounds = layer_get_bounds(layer); 36 | graphics_context_set_fill_color(ctx, GColorBlack); 37 | graphics_fill_rect(ctx, bounds, 5, GCornersAll); 38 | graphics_context_set_fill_color(ctx, GColorWhite); 39 | graphics_fill_rect(ctx, GRect(bounds.origin.x + 2, bounds.origin.y + 2, bounds.size.w - 4, bounds.size.h - 4), 5, GCornersAll); 40 | graphics_context_set_text_color(ctx, GColorBlack); 41 | graphics_draw_text(ctx, data->message, s_font, GRect(bounds.origin.x + 5, bounds.origin.y, bounds.size.w - 10, bounds.size.h - 10), GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); 42 | } 43 | -------------------------------------------------------------------------------- /src/c/title_layer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "title_layer.h" 3 | #include "letter_layer.h" 4 | 5 | typedef struct { 6 | LetterLayer *letters[6]; 7 | LetterStatus statuses[6]; 8 | int last_flip; 9 | AppTimer *timer; 10 | } TitleLayerData; 11 | 12 | static char *s_wordle = "wordle"; 13 | 14 | static void prv_schedule_timer(TitleLayer *layer); 15 | static void prv_timer_callback(void *context); 16 | 17 | TitleLayer *title_layer_create(GPoint where, bool animated) { 18 | Layer *layer = layer_create_with_data(GRect(where.x, where.y, 6 * (LETTER_LAYER_SIZE + 1), LETTER_LAYER_SIZE), sizeof(TitleLayerData)); 19 | TitleLayerData *data = layer_get_data(layer); 20 | 21 | for (int i = 0; i < 6; ++i) { 22 | data->letters[i] = letter_layer_create(GPoint(i * (LETTER_LAYER_SIZE + 1), 0)); 23 | layer_add_child(layer, data->letters[i]); 24 | letter_layer_set_letter(data->letters[i], 0, LetterStatusNeutral, true, false); 25 | letter_layer_set_letter(data->letters[i], s_wordle[i], LetterStatusNeutral, true, i+1); 26 | } 27 | 28 | if (animated) { 29 | data->timer = app_timer_register(2500, prv_timer_callback, layer); 30 | } 31 | 32 | return layer; 33 | } 34 | 35 | void title_layer_destroy(TitleLayer *layer) { 36 | TitleLayerData *data = layer_get_data(layer); 37 | app_timer_cancel(data->timer); 38 | for (int i = 0; i < 6; ++i) { 39 | letter_layer_destroy(data->letters[i]); 40 | } 41 | } 42 | 43 | static void prv_schedule_timer(TitleLayer *layer) { 44 | TitleLayerData *data = layer_get_data(layer); 45 | data->timer = app_timer_register(rand() % 2000 + 1000, prv_timer_callback, layer); 46 | } 47 | 48 | static void prv_timer_callback(void *context) { 49 | TitleLayer *layer = (TitleLayer *)context; 50 | TitleLayerData *data = layer_get_data(layer); 51 | int i; 52 | do { 53 | i = rand() % 6; 54 | } while (i == data->last_flip); 55 | data->last_flip = i; 56 | LetterStatus status; 57 | do { 58 | status = rand() % LetterStatusCount; 59 | } while (status == data->statuses[i]); 60 | data->statuses[i] = status; 61 | letter_layer_set_letter(data->letters[i], s_wordle[i], status, true, true); 62 | prv_schedule_timer(layer); 63 | } 64 | -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | # 2 | # This file is the default set of rules to compile a Pebble application. 3 | # 4 | # Feel free to customize this to your needs. 5 | # 6 | import os.path 7 | 8 | top = '.' 9 | out = 'build' 10 | 11 | 12 | def options(ctx): 13 | ctx.load('pebble_sdk') 14 | 15 | 16 | def configure(ctx): 17 | """ 18 | This method is used to configure your build. ctx.load(`pebble_sdk`) automatically configures 19 | a build for each valid platform in `targetPlatforms`. Platform-specific configuration: add your 20 | change after calling ctx.load('pebble_sdk') and make sure to set the correct environment first. 21 | Universal configuration: add your change prior to calling ctx.load('pebble_sdk'). 22 | """ 23 | ctx.load('pebble_sdk') 24 | 25 | 26 | def build(ctx): 27 | ctx.load('pebble_sdk') 28 | 29 | build_worker = os.path.exists('worker_src') 30 | binaries = [] 31 | 32 | cached_env = ctx.env 33 | for platform in ctx.env.TARGET_PLATFORMS: 34 | ctx.env = ctx.all_envs[platform] 35 | # pebble-events apparently triggers this 36 | ctx.env.CFLAGS.append('-Wno-expansion-to-defined') 37 | ctx.set_group(ctx.env.PLATFORM_NAME) 38 | app_elf = '{}/pebble-app.elf'.format(ctx.env.BUILD_DIR) 39 | ctx.pbl_build(source=ctx.path.ant_glob('src/c/**/*.c'), target=app_elf, bin_type='app') 40 | 41 | if build_worker: 42 | worker_elf = '{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR) 43 | binaries.append({'platform': platform, 'app_elf': app_elf, 'worker_elf': worker_elf}) 44 | ctx.pbl_build(source=ctx.path.ant_glob('worker_src/c/**/*.c'), 45 | target=worker_elf, 46 | bin_type='worker') 47 | else: 48 | binaries.append({'platform': platform, 'app_elf': app_elf}) 49 | ctx.env = cached_env 50 | 51 | ctx.set_group('bundle') 52 | ctx.pbl_bundle(binaries=binaries, 53 | js=ctx.path.ant_glob(['src/pkjs/**/*.js', 54 | 'src/pkjs/**/*.json', 55 | 'src/common/**/*.js']), 56 | js_entry_file='src/pkjs/index.js') 57 | -------------------------------------------------------------------------------- /src/c/stat_tracker.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "stat_tracker.h" 3 | #include "model.h" 4 | 5 | #define STAT_TRACKER_VERSION_KEY 3 6 | #define STAT_TRACKER_CONTENT_KEY 4 7 | 8 | struct StatTracker { 9 | short current_streak; 10 | short max_streak; 11 | short last_wordle; 12 | short distribution[7]; 13 | } __attribute__((packed)); 14 | 15 | StatTracker *stat_tracker_load() { 16 | StatTracker *tracker = malloc(sizeof(StatTracker)); 17 | if (persist_read_int(STAT_TRACKER_VERSION_KEY) == 1) { 18 | persist_read_data(STAT_TRACKER_CONTENT_KEY, tracker, sizeof(StatTracker)); 19 | } else { 20 | memset(tracker, 0, sizeof(StatTracker)); 21 | } 22 | return tracker; 23 | } 24 | 25 | void stat_tracker_destroy(StatTracker *stat_tracker) { 26 | free(stat_tracker); 27 | } 28 | 29 | void stat_tracker_record_result(StatTracker *stat_tracker, int wordle_num, int result) { 30 | if (result > 0) { 31 | if (stat_tracker->last_wordle >= wordle_num - 1) { 32 | stat_tracker->current_streak++; 33 | } else { 34 | stat_tracker->current_streak = 1; 35 | } 36 | if (stat_tracker->current_streak > stat_tracker->max_streak) { 37 | stat_tracker->max_streak = stat_tracker->current_streak; 38 | } 39 | stat_tracker->last_wordle = wordle_num; 40 | } else { 41 | stat_tracker->current_streak = 0; 42 | } 43 | stat_tracker->distribution[result]++; 44 | persist_write_int(STAT_TRACKER_VERSION_KEY, 1); 45 | persist_write_data(STAT_TRACKER_CONTENT_KEY, stat_tracker, sizeof(StatTracker)); 46 | } 47 | 48 | int stat_tracker_get_current_streak(StatTracker *tracker) { 49 | return tracker->last_wordle >= wordle_number() - 1 ? tracker->current_streak : 0; 50 | } 51 | 52 | int stat_tracker_get_max_streak(StatTracker *tracker) { 53 | return tracker->max_streak; 54 | } 55 | 56 | int stat_tracker_get_total_played(StatTracker *tracker) { 57 | int total = 0; 58 | for (int i = 0; i < 7; ++i) { 59 | total += tracker->distribution[i]; 60 | } 61 | return total; 62 | } 63 | 64 | int stat_tracker_get_win_percent(StatTracker *tracker) { 65 | int percent = tracker->distribution[0] * 100; 66 | int total_played = stat_tracker_get_total_played(tracker); 67 | if (total_played == 0) { 68 | return 100; 69 | } 70 | percent /= total_played; 71 | percent = 100 - percent; 72 | return percent; 73 | } 74 | 75 | int stat_tracker_get_distribution_bucket(StatTracker *tracker, int bucket) { 76 | return tracker->distribution[bucket]; 77 | } 78 | -------------------------------------------------------------------------------- /src/c/word_layer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "word_layer.h" 3 | #include "model.h" 4 | #include "letter_layer.h" 5 | #include "cursor_layer.h" 6 | 7 | #define LETTER_PADDING 3 8 | 9 | typedef struct { 10 | LetterLayer *letter_layers[WORD_LENGTH]; 11 | char word[WORD_LENGTH]; 12 | int cursor_pos; 13 | CursorLayer *cursor_layer; 14 | } WordLayerData; 15 | 16 | WordLayer *word_layer_create(GPoint where) { 17 | GRect size = GRect(where.x, where.y, (LETTER_LAYER_SIZE + LETTER_PADDING) * WORD_LENGTH - LETTER_PADDING + 2, LETTER_LAYER_SIZE + 2); 18 | Layer *layer = layer_create_with_data(size, sizeof(WordLayerData)); 19 | WordLayerData *data = layer_get_data(layer); 20 | for (int i = 0; i < WORD_LENGTH; ++i) { 21 | data->letter_layers[i] = letter_layer_create(GPoint(1 + i * (LETTER_LAYER_SIZE + LETTER_PADDING), 1)); 22 | layer_add_child(layer, data->letter_layers[i]); 23 | } 24 | data->cursor_pos = -1; 25 | data->cursor_layer = NULL; 26 | return layer; 27 | } 28 | 29 | void word_layer_destroy(WordLayer *layer) { 30 | WordLayerData *data = layer_get_data(layer); 31 | for (int i = 0; i < WORD_LENGTH; ++i) { 32 | letter_layer_destroy(data->letter_layers[i]); 33 | } 34 | if (data->cursor_layer) { 35 | cursor_layer_destroy(data->cursor_layer); 36 | data->cursor_layer = NULL; 37 | } 38 | layer_destroy(layer); 39 | } 40 | 41 | void word_layer_set_letter(WordLayer *layer, int index, char letter, LetterStatus status, bool confirmed) { 42 | WordLayerData *data = layer_get_data(layer); 43 | data->word[index] = letter; 44 | letter_layer_set_letter(data->letter_layers[index], letter, status, confirmed, false); 45 | } 46 | 47 | void word_layer_set_cursor(WordLayer *layer, int index) { 48 | WordLayerData *data = layer_get_data(layer); 49 | data->cursor_pos = index; 50 | if (data->cursor_layer) { 51 | layer_remove_from_parent(data->cursor_layer); 52 | cursor_layer_destroy(data->cursor_layer); 53 | data->cursor_layer = NULL; 54 | } 55 | if (index >= 0) { 56 | data->cursor_layer = cursor_layer_create(GPoint(index * (LETTER_LAYER_SIZE + LETTER_PADDING), 0)); 57 | layer_add_child(layer, data->cursor_layer); 58 | } 59 | } 60 | 61 | void word_layer_apply_score(WordLayer *layer, LetterStatus statuses[WORD_LENGTH], bool animated) { 62 | WordLayerData *data = layer_get_data(layer); 63 | for (int i = 0; i < WORD_LENGTH; ++i) { 64 | letter_layer_set_letter(data->letter_layers[i], data->word[i], statuses[i], true, animated ? i + 1 : 0); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/c/distribution_layer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "distribution_layer.h" 3 | #include "stat_tracker.h" 4 | #include "game.h" 5 | 6 | typedef struct { 7 | StatTracker *stat_tracker; 8 | } DistributionLayerData; 9 | 10 | static void prv_update_proc(Layer *layer, GContext *ctx); 11 | 12 | DistributionLayer *distribution_layer_create(GRect frame, StatTracker *stat_tracker) { 13 | Layer *layer = layer_create_with_data(frame, sizeof(DistributionLayerData)); 14 | DistributionLayerData *data = layer_get_data(layer); 15 | data->stat_tracker = stat_tracker; 16 | layer_set_update_proc(layer, prv_update_proc); 17 | return layer; 18 | } 19 | 20 | void distribution_layer_destroy(DistributionLayer *layer) { 21 | layer_destroy(layer); 22 | } 23 | 24 | static void prv_update_proc(Layer *layer, GContext *ctx) { 25 | DistributionLayerData *data = layer_get_data(layer); 26 | 27 | graphics_context_set_text_color(ctx, GColorBlack); 28 | GRect bounds = layer_get_bounds(layer); 29 | int per_bar_height = bounds.size.h / GUESS_LIMIT; 30 | int left_pad = 12; 31 | int max_bar_width = bounds.size.w - left_pad; 32 | GFont label_font = fonts_get_system_font(FONT_KEY_GOTHIC_18); 33 | GFont value_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); 34 | 35 | int highest_bucket_value = 0; 36 | 37 | for (int i = 0; i < GUESS_LIMIT; ++i) { 38 | char digit[2] = {'0' + i+1, 0}; 39 | graphics_draw_text(ctx, digit, label_font, GRect(-2, per_bar_height * i - 4, 10, per_bar_height), GTextOverflowModeWordWrap, GTextAlignmentRight, NULL); 40 | int value = stat_tracker_get_distribution_bucket(data->stat_tracker, i+1); 41 | if (value > highest_bucket_value) { 42 | highest_bucket_value = value; 43 | } 44 | } 45 | 46 | char value_str[5]; 47 | int last_score = -1; 48 | if (game_get_status() == GameStatusWon) { 49 | last_score = game_get_guess_number(); 50 | } 51 | for (int i = 0; i < GUESS_LIMIT; ++i) { 52 | int value = stat_tracker_get_distribution_bucket(data->stat_tracker, i+1); 53 | snprintf(value_str, sizeof(value_str), "%d", value); 54 | int width = (max_bar_width * value) / highest_bucket_value; 55 | if (width < 15) { 56 | width = 15; 57 | } 58 | graphics_context_set_fill_color(ctx, last_score == i ? GColorDarkGreen : GColorDarkGray); 59 | graphics_fill_rect(ctx, GRect(left_pad, per_bar_height * i + 1, width, per_bar_height - 1), 0, GCornerNone); 60 | graphics_context_set_text_color(ctx, GColorWhite); 61 | graphics_draw_text(ctx, value_str, value_font, GRect(left_pad, per_bar_height * i - 2, width - 3, per_bar_height), GTextOverflowModeWordWrap, GTextAlignmentRight, NULL); 62 | } 63 | } -------------------------------------------------------------------------------- /src/c/share_layer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "share_layer.h" 3 | #include "game.h" 4 | #include "model.h" 5 | #include "vendor/qrcodegen.h" 6 | 7 | // This needs to be in ALL CAPS so we can use the more efficient 8 | // QR encoding. 9 | #define URL_PREFIX "HTTPS://WORD.KTBY.IO/" 10 | 11 | typedef struct { 12 | bool has_qr_code; 13 | uint8_t qrcode[qrcodegen_BUFFER_LEN_FOR_VERSION(2)]; 14 | } ShareLayerData; 15 | 16 | static void prv_silly_number_encoding(int number, char* buffer); 17 | static void prv_silly_short_encoding(uint16_t word, char* buffer); 18 | static void prv_update_proc(Layer *layer, GContext *ctx); 19 | static bool prv_generate_alphanumeric_qr(char *text, uint8_t *qrcode); 20 | 21 | ShareLayer *share_layer_create(GRect frame) { 22 | Layer *layer = layer_create_with_data(frame, sizeof(ShareLayerData)); 23 | layer_set_update_proc(layer, prv_update_proc); 24 | return layer; 25 | } 26 | 27 | void share_layer_set_game_state(ShareLayer *layer, int wordle_num, LetterStatus guesses[GUESS_LIMIT][WORD_LENGTH]) { 28 | ShareLayerData *data = layer_get_data(layer); 29 | char path[14]; 30 | memset(path, 0, sizeof(path)); 31 | prv_silly_number_encoding(wordle_num, path); 32 | for (int i = 0; i < GUESS_LIMIT; ++i) { 33 | if (guesses[i][0] == LetterStatusNeutral) { 34 | break; 35 | } 36 | uint16_t guess = 0; 37 | for (int j = 0; j < WORD_LENGTH; ++j) { 38 | guess |= ((uint8_t)guesses[i][j] << (j * 2)); 39 | } 40 | prv_silly_short_encoding(guess, path + 3 + i*2); 41 | } 42 | size_t url_length = sizeof(URL_PREFIX) + strlen(path); 43 | char* url = malloc(url_length); 44 | snprintf(url, url_length, URL_PREFIX "%s", path); 45 | 46 | data->has_qr_code = prv_generate_alphanumeric_qr(url, data->qrcode); 47 | free(url); 48 | } 49 | 50 | void share_layer_destroy(ShareLayer *layer) { 51 | layer_destroy(layer); 52 | } 53 | 54 | static bool prv_generate_alphanumeric_qr(char *text, uint8_t *qrcode) { 55 | uint8_t *temp_buffer = malloc(qrcodegen_BUFFER_LEN_FOR_VERSION(2)); 56 | struct qrcodegen_Segment seg; 57 | seg = qrcodegen_makeAlphanumeric(text, temp_buffer); 58 | bool result = qrcodegen_encodeSegmentsAdvanced(&seg, 1, qrcodegen_Ecc_MEDIUM, 2, 2, qrcodegen_Mask_AUTO, true, temp_buffer, qrcode); 59 | free(temp_buffer); 60 | return result; 61 | } 62 | 63 | static void prv_silly_number_encoding(int number, char* buffer) { 64 | const char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 65 | buffer[0] = alphabet[number / (36 * 36)]; 66 | buffer[1] = alphabet[(number % (36 * 36)) / 36]; 67 | buffer[2] = alphabet[number % 36]; 68 | } 69 | 70 | static void prv_silly_short_encoding(uint16_t word, char* buffer) { 71 | const char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 72 | if (word > 36 * 36) { 73 | APP_LOG(APP_LOG_LEVEL_ERROR, "%d is larger than 1296, the largest number we can encode.", word); 74 | buffer[0] = '9'; 75 | buffer[1] = '9'; 76 | return; 77 | } 78 | buffer[0] = alphabet[word / 36]; 79 | buffer[1] = alphabet[word % 36]; 80 | } 81 | 82 | static void prv_update_proc(Layer *layer, GContext *ctx) { 83 | ShareLayerData *data = layer_get_data(layer); 84 | if (!data->has_qr_code) { 85 | graphics_context_set_fill_color(ctx, GColorRed); 86 | graphics_fill_rect(ctx, layer_get_bounds(layer), 0, GCornerNone); 87 | return; 88 | } 89 | 90 | int modules_per_side = qrcodegen_getSize(data->qrcode); 91 | GRect bounds = layer_get_bounds(layer); 92 | int ppm = (bounds.size.w < bounds.size.h ? bounds.size.w : bounds.size.h) / modules_per_side; 93 | int offset_x = (bounds.size.w - ppm * modules_per_side) / 2; 94 | int offset_y = (bounds.size.h - ppm * modules_per_side) / 2; 95 | for (int i = 0; i < modules_per_side; ++i) { 96 | for (int j = 0; j < modules_per_side; ++j) { 97 | GColor color = qrcodegen_getModule(data->qrcode, i, j) ? GColorBlack : GColorWhite; 98 | graphics_context_set_fill_color(ctx, color); 99 | graphics_fill_rect(ctx, GRect(offset_x + i * ppm, offset_y + j * ppm, ppm, ppm), 0, GCornerNone); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /emoji-site/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Copy your score 8 | 43 | 44 | 45 |
46 | 47 | 48 | 49 | 130 | 131 | -------------------------------------------------------------------------------- /src/c/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "model.h" 5 | #include "title_layer.h" 6 | #include "game.h" 7 | #include "stat_window.h" 8 | #include "help_window.h" 9 | 10 | #define MENU_OPTIONS 3 11 | 12 | static Window *s_window; 13 | static TitleLayer *s_title; 14 | static TextLayer *s_number; 15 | static char *s_menu_text[MENU_OPTIONS] = {"Play", "Stats", "Help"}; 16 | static TextLayer *s_menu_options[MENU_OPTIONS]; 17 | static char s_number_text[15]; 18 | static int s_selected_option = 0; 19 | static int s_wordle_number; 20 | static EventHandle s_tick_timer_handle; 21 | 22 | static void prv_click_config_provider(void *ctx); 23 | static void prv_construct_menu(); 24 | static void prv_update_menu(); 25 | static void prv_handle_select(ClickRecognizerRef recognizer, void *ctx); 26 | static void prv_handle_up(ClickRecognizerRef recognizer, void *ctx); 27 | static void prv_handle_down(ClickRecognizerRef recognizer, void *ctx); 28 | static void prv_handle_scroll(int direction); 29 | static void prv_window_load(Window *window); 30 | static void prv_window_unload(Window *window); 31 | static void prv_deinit(); 32 | static void prv_handle_day_change(struct tm *tick_time, TimeUnits units_changed); 33 | 34 | 35 | static void prv_window_load(Window *window) { 36 | s_title = title_layer_create(GPoint(1, 5), true); 37 | layer_add_child(window_get_root_layer(window), s_title); 38 | s_number = text_layer_create(GRect(0, 151, 140, 16)); 39 | text_layer_set_text_alignment(s_number, GTextAlignmentRight); 40 | s_wordle_number = wordle_number(); 41 | snprintf(s_number_text, 15, "Wordle #%d", s_wordle_number); 42 | text_layer_set_text(s_number, s_number_text); 43 | layer_add_child(window_get_root_layer(window), (Layer *)s_number); 44 | prv_construct_menu(); 45 | } 46 | 47 | static void prv_construct_menu() { 48 | for (int i = 0; i < MENU_OPTIONS; ++i) { 49 | s_menu_options[i] = text_layer_create(GRect(0, 40 + 35 * i, 144, 35)); 50 | text_layer_set_text_alignment(s_menu_options[i], GTextAlignmentCenter); 51 | text_layer_set_text(s_menu_options[i], s_menu_text[i]); 52 | layer_add_child(window_get_root_layer(s_window), (Layer *)s_menu_options[i]); 53 | } 54 | prv_update_menu(); 55 | } 56 | 57 | static void prv_update_menu() { 58 | for (int i = 0; i < MENU_OPTIONS; ++i) { 59 | if (i == s_selected_option) { 60 | text_layer_set_font(s_menu_options[i], fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); 61 | text_layer_set_background_color(s_menu_options[i], GColorMintGreen); 62 | } else { 63 | text_layer_set_font(s_menu_options[i], fonts_get_system_font(FONT_KEY_GOTHIC_24)); 64 | text_layer_set_background_color(s_menu_options[i], GColorClear); 65 | } 66 | } 67 | } 68 | 69 | static void prv_window_unload(Window *window) { 70 | title_layer_destroy(s_title); 71 | for (int i = 0; i < MENU_OPTIONS; ++i) { 72 | text_layer_destroy(s_menu_options[i]); 73 | } 74 | } 75 | 76 | static void prv_init() { 77 | s_window = window_create(); 78 | window_set_window_handlers(s_window, (WindowHandlers) { 79 | .load = prv_window_load, 80 | .unload = prv_window_unload, 81 | }); 82 | window_set_click_config_provider(s_window, prv_click_config_provider); 83 | window_stack_push(s_window, true); 84 | s_tick_timer_handle = events_tick_timer_service_subscribe(DAY_UNIT, prv_handle_day_change); 85 | } 86 | 87 | static void prv_deinit() { 88 | events_tick_timer_service_unsubscribe(s_tick_timer_handle); 89 | } 90 | 91 | static void prv_handle_day_change(struct tm *tick_time, TimeUnits units_changed) { 92 | if (units_changed & DAY_UNIT) { 93 | if (s_wordle_number != wordle_number()) { 94 | window_stack_pop_all(true); 95 | } 96 | } 97 | } 98 | 99 | static void prv_click_config_provider(void *ctx) { 100 | window_single_click_subscribe(BUTTON_ID_SELECT, prv_handle_select); 101 | window_single_click_subscribe(BUTTON_ID_UP, prv_handle_up); 102 | window_single_click_subscribe(BUTTON_ID_DOWN, prv_handle_down); 103 | } 104 | 105 | static void prv_handle_select(ClickRecognizerRef recognizer, void *ctx) { 106 | switch (s_selected_option) { 107 | case 0: 108 | show_game(); 109 | break; 110 | case 1: 111 | stat_window_push(); 112 | break; 113 | case 2: 114 | help_window_push(); 115 | break; 116 | } 117 | } 118 | 119 | static void prv_handle_up(ClickRecognizerRef recognizer, void *ctx) { 120 | prv_handle_scroll(-1); 121 | } 122 | 123 | static void prv_handle_down(ClickRecognizerRef recognizer, void *ctx) { 124 | prv_handle_scroll(1); 125 | } 126 | 127 | static void prv_handle_scroll(int direction) { 128 | s_selected_option += direction; 129 | if (s_selected_option < 0) { 130 | s_selected_option = MENU_OPTIONS - 1; 131 | } else if (s_selected_option >= MENU_OPTIONS) { 132 | s_selected_option = 0; 133 | } 134 | prv_update_menu(); 135 | } 136 | 137 | int main(void) { 138 | prv_init(); 139 | app_event_loop(); 140 | prv_deinit(); 141 | } 142 | -------------------------------------------------------------------------------- /src/c/model.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "model.h" 3 | 4 | static short prv_words_in_file(ResHandle file); 5 | static short prv_accepted_words(); 6 | static void prv_accepted_at_index(int index, char buffer[WORD_LENGTH]); 7 | static int prv_compare_words(char a[WORD_LENGTH], char b[WORD_LENGTH]); 8 | static int prv_letter_count(char word[WORD_LENGTH], int limit, char letter); 9 | static int prv_correct_letter_count(char word[WORD_LENGTH], char expected[WORD_LENGTH], char letter); 10 | static void prv_word_for_day(int day, char word[WORD_LENGTH]); 11 | static void prv_word_at_index(ResHandle file, int index, char buffer[WORD_LENGTH]); 12 | 13 | bool score_word(char word[WORD_LENGTH], char expected[WORD_LENGTH], LetterStatus result[WORD_LENGTH]) { 14 | bool correct = true; 15 | 16 | for (int i = 0; i < WORD_LENGTH; ++i) { 17 | if (word[i] == expected[i]) { 18 | result[i] = LetterStatusCorrect; 19 | continue; 20 | } 21 | correct = false; 22 | if (prv_letter_count(expected, WORD_LENGTH, word[i]) - prv_correct_letter_count(word, expected, word[i]) > prv_letter_count(word, i, word[i])) { 23 | result[i] = LetterStatusWrongPosition; 24 | } else { 25 | result[i] = LetterStatusNotPresent; 26 | } 27 | } 28 | 29 | return correct; 30 | } 31 | 32 | bool is_valid_word(char word[WORD_LENGTH]) { 33 | int total_words = prv_accepted_words(); 34 | int pos = total_words / 2; 35 | int lower_bound = 0; 36 | int upper_bound = total_words - 1; 37 | 38 | while (lower_bound <= upper_bound) { 39 | char comp_word[WORD_LENGTH]; 40 | prv_accepted_at_index(pos, comp_word); 41 | int comp = prv_compare_words(word, comp_word); 42 | if (comp == 0) { 43 | return true; 44 | } 45 | if (comp < 0) { 46 | upper_bound = pos - 1; 47 | pos = (lower_bound + upper_bound) / 2; 48 | } else { 49 | lower_bound = pos + 1; 50 | pos = (lower_bound + upper_bound) / 2; 51 | } 52 | } 53 | 54 | return false; 55 | } 56 | 57 | void word_of_the_day(char word[WORD_LENGTH]) { 58 | prv_word_for_day(wordle_number(), word); 59 | } 60 | 61 | static int prv_compare_tm(struct tm *a, struct tm *b) { 62 | if (a->tm_year > b->tm_year) { 63 | return 1; 64 | } else if (a->tm_year < b->tm_year) { 65 | return -1; 66 | } 67 | if (a->tm_mon > b->tm_mon) { 68 | return 1; 69 | } else if(a->tm_mon < b->tm_mon) { 70 | return -1; 71 | } 72 | if (a->tm_mday > b->tm_mday) { 73 | return 1; 74 | } else if(a->tm_mday < b->tm_mday) { 75 | return -1; 76 | } 77 | if (a->tm_hour > b->tm_hour) { 78 | return 1; 79 | } else if (a->tm_hour < b->tm_hour) { 80 | return -1; 81 | } 82 | if (a->tm_min > b->tm_min) { 83 | return 1; 84 | } else if(a->tm_min < b->tm_min) { 85 | return -1; 86 | } 87 | return 0; 88 | } 89 | 90 | static time_t prv_tm_diff(struct tm *a, struct tm *b) { 91 | int compare = prv_compare_tm(a, b); 92 | if (compare == 0) { 93 | return 0; 94 | } 95 | if (compare < 0) { 96 | struct tm *temp = a; 97 | a = b; 98 | b = temp; 99 | } 100 | int difference = (a->tm_hour - b->tm_hour) * 3600 + (a->tm_min - b->tm_min) * 60; 101 | if(a->tm_mday != b->tm_mday) { 102 | difference += 86400; 103 | } 104 | if (compare > 0) { 105 | return difference; 106 | } 107 | return -difference; 108 | } 109 | 110 | static time_t prv_timezone_offset() { 111 | time_t now = time(NULL); 112 | struct tm local = *localtime(&now); 113 | struct tm utc = *gmtime(&now); 114 | return prv_tm_diff(&local, &utc); 115 | } 116 | 117 | int wordle_number() { 118 | // Wordle day 0 is June 19th, 2021 in the user's local time 119 | time_t epoch_unixtime = 1624060800; 120 | 121 | time_t now = time(NULL); 122 | time_t tz_offset = prv_timezone_offset(); 123 | now += tz_offset; 124 | int days = (int)(now - epoch_unixtime) / 86400; 125 | return days; 126 | } 127 | 128 | static void prv_word_for_day(int day, char word[WORD_LENGTH]) { 129 | prv_word_at_index(resource_get_handle(RESOURCE_ID_SOLUTION_WORDS), day, word); 130 | } 131 | 132 | static int prv_letter_count(char word[WORD_LENGTH], int limit, char letter) { 133 | int count = 0; 134 | for (int i = 0; i < limit; ++i) { 135 | if (word[i] == letter) { 136 | ++count; 137 | } 138 | } 139 | return count; 140 | } 141 | 142 | static int prv_correct_letter_count(char word[WORD_LENGTH], char expected[WORD_LENGTH], char letter) { 143 | int count = 0; 144 | for (int i = 0; i < WORD_LENGTH; ++i) { 145 | if (expected[i] == letter && word[i] == letter) { 146 | ++count; 147 | } 148 | } 149 | return count; 150 | } 151 | 152 | static void prv_word_at_index(ResHandle file, int index, char buffer[WORD_LENGTH]) { 153 | resource_load_byte_range(file, index * WORD_LENGTH, (uint8_t*)buffer, WORD_LENGTH); 154 | } 155 | 156 | static short prv_words_in_file(ResHandle file) { 157 | return resource_size(file) / WORD_LENGTH; 158 | } 159 | 160 | static short prv_accepted_words() { 161 | return prv_words_in_file(resource_get_handle(RESOURCE_ID_ACCEPTABLE_WORDS)); 162 | } 163 | 164 | static void prv_accepted_at_index(int index, char buffer[WORD_LENGTH]) { 165 | prv_word_at_index(resource_get_handle(RESOURCE_ID_ACCEPTABLE_WORDS), index, buffer); 166 | } 167 | 168 | static int prv_compare_words(char a[WORD_LENGTH], char b[WORD_LENGTH]) { 169 | for (int i = 0; i < WORD_LENGTH; ++i) { 170 | if (a[i] < b[i]) { 171 | return -1; 172 | } 173 | if (a[i] > b[i]) { 174 | return 1; 175 | } 176 | } 177 | return 0; 178 | } -------------------------------------------------------------------------------- /src/c/help_window.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "help_window.h" 3 | #include "word_layer.h" 4 | 5 | static Window *s_window; 6 | static ScrollLayer *s_scroll_layer; 7 | static ContentIndicator *s_content_indicator; 8 | static Layer *s_down_arrow; 9 | static Layer *s_up_arrow; 10 | static TextLayer *s_explanation_layer; 11 | static WordLayer *s_example1_word; 12 | static TextLayer *s_example1_label; 13 | static WordLayer *s_example2_word; 14 | static TextLayer *s_example2_label; 15 | static WordLayer *s_example3_word; 16 | static TextLayer *s_example3_label; 17 | 18 | static void prv_window_load(Window *window); 19 | static void prv_window_unload(Window *window); 20 | 21 | void help_window_push() { 22 | s_window = window_create(); 23 | window_set_window_handlers(s_window, (WindowHandlers) { 24 | .load = prv_window_load, 25 | .unload = prv_window_unload, 26 | }); 27 | window_stack_push(s_window, true); 28 | } 29 | 30 | static void prv_window_load(Window *window) { 31 | s_scroll_layer = scroll_layer_create(layer_get_bounds(window_get_root_layer(window))); 32 | layer_add_child(window_get_root_layer(window), (Layer *)s_scroll_layer); 33 | scroll_layer_set_shadow_hidden(s_scroll_layer, true); 34 | scroll_layer_set_click_config_onto_window(s_scroll_layer, window); 35 | 36 | s_content_indicator = scroll_layer_get_content_indicator(s_scroll_layer); 37 | s_up_arrow = layer_create(GRect(0, 0, 144, 15)); 38 | s_down_arrow = layer_create(GRect(0, 153, 144, 15)); 39 | layer_add_child(window_get_root_layer(window), s_up_arrow); 40 | layer_add_child(window_get_root_layer(window), s_down_arrow); 41 | 42 | const ContentIndicatorConfig up_config = (ContentIndicatorConfig) { 43 | .layer = s_up_arrow, 44 | .times_out = false, 45 | .alignment = GAlignCenter, 46 | .colors = { 47 | .foreground = GColorBlack, 48 | .background = GColorWhite 49 | } 50 | }; 51 | content_indicator_configure_direction(s_content_indicator, ContentIndicatorDirectionUp, &up_config); 52 | 53 | const ContentIndicatorConfig down_config = (ContentIndicatorConfig) { 54 | .layer = s_down_arrow, 55 | .times_out = false, 56 | .alignment = GAlignCenter, 57 | .colors = { 58 | .foreground = GColorBlack, 59 | .background = GColorWhite 60 | } 61 | }; 62 | content_indicator_configure_direction(s_content_indicator, ContentIndicatorDirectionDown, &down_config); 63 | 64 | scroll_layer_set_content_size(s_scroll_layer, GSize(144, 1120)); 65 | 66 | GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_24); 67 | 68 | s_explanation_layer = text_layer_create(GRect(5, 0, 134, 685)); 69 | text_layer_set_font(s_explanation_layer, font); 70 | text_layer_set_text(s_explanation_layer, "Guess the WORDLE in six tries.\n\n" 71 | "Each guess must be a valid five-letter word. Cycle through the letters using the up and down buttons, and hit select to confirm. " 72 | "You can also press select on an empty space to fill in the letter immediately above. Press back to go back a letter. " 73 | "Press select at the end of the word to submit.\n\n" 74 | "After each guess, the color of the tiles will change to show how close your guess was to the word.\n\n" 75 | "Examples:"); 76 | scroll_layer_add_child(s_scroll_layer, (Layer *)s_explanation_layer); 77 | 78 | s_example1_word = word_layer_create(GPoint(7, 685)); 79 | word_layer_set_letter(s_example1_word, 0, 'w', LetterStatusCorrect, true); 80 | word_layer_set_letter(s_example1_word, 1, 'e', LetterStatusNeutral, true); 81 | word_layer_set_letter(s_example1_word, 2, 'a', LetterStatusNeutral, true); 82 | word_layer_set_letter(s_example1_word, 3, 'r', LetterStatusNeutral, true); 83 | word_layer_set_letter(s_example1_word, 4, 'y', LetterStatusNeutral, true); 84 | scroll_layer_add_child(s_scroll_layer, (Layer *)s_example1_word); 85 | 86 | s_example1_label = text_layer_create(GRect(5, 710, 134, 80)); 87 | text_layer_set_font(s_example1_label, font); 88 | text_layer_set_text(s_example1_label, "The letter W is in the word and in the correct spot."); 89 | scroll_layer_add_child(s_scroll_layer, (Layer *)s_example1_label); 90 | 91 | s_example2_word = word_layer_create(GPoint(7, 800)); 92 | word_layer_set_letter(s_example2_word, 0, 'p', LetterStatusNeutral, true); 93 | word_layer_set_letter(s_example2_word, 1, 'i', LetterStatusWrongPosition, true); 94 | word_layer_set_letter(s_example2_word, 2, 'l', LetterStatusNeutral, true); 95 | word_layer_set_letter(s_example2_word, 3, 'l', LetterStatusNeutral, true); 96 | word_layer_set_letter(s_example2_word, 4, 's', LetterStatusNeutral, true); 97 | scroll_layer_add_child(s_scroll_layer, (Layer *)s_example2_word); 98 | 99 | s_example2_label = text_layer_create(GRect(5, 825, 134, 80)); 100 | text_layer_set_font(s_example2_label, font); 101 | text_layer_set_text(s_example2_label, "The letter I is in the word but in the wrong spot."); 102 | scroll_layer_add_child(s_scroll_layer, (Layer *)s_example2_label); 103 | 104 | s_example3_word = word_layer_create(GPoint(7, 915)); 105 | word_layer_set_letter(s_example3_word, 0, 'v', LetterStatusNeutral, true); 106 | word_layer_set_letter(s_example3_word, 1, 'a', LetterStatusNeutral, true); 107 | word_layer_set_letter(s_example3_word, 2, 'g', LetterStatusNeutral, true); 108 | word_layer_set_letter(s_example3_word, 3, 'u', LetterStatusNotPresent, true); 109 | word_layer_set_letter(s_example3_word, 4, 'e', LetterStatusNeutral, true); 110 | scroll_layer_add_child(s_scroll_layer, (Layer *)s_example3_word); 111 | 112 | s_example3_label = text_layer_create(GRect(5, 940, 134, 180)); 113 | text_layer_set_font(s_example3_label, font); 114 | text_layer_set_text(s_example3_label, "The letter U is not in the word in any spot.\n\nA new WORDLE will be available each day!"); 115 | scroll_layer_add_child(s_scroll_layer, (Layer *)s_example3_label); 116 | } 117 | 118 | static void prv_window_unload(Window *window) { 119 | layer_destroy(s_up_arrow); 120 | layer_destroy(s_down_arrow); 121 | word_layer_destroy(s_example1_word); 122 | text_layer_destroy(s_example1_label); 123 | word_layer_destroy(s_example2_word); 124 | text_layer_destroy(s_example2_label); 125 | word_layer_destroy(s_example3_word); 126 | text_layer_destroy(s_example3_label); 127 | text_layer_destroy(s_explanation_layer); 128 | scroll_layer_destroy(s_scroll_layer); 129 | } 130 | -------------------------------------------------------------------------------- /src/c/letter_layer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "letter_layer.h" 3 | #include "model.h" 4 | 5 | 6 | typedef struct { 7 | char letter; 8 | LetterStatus status; 9 | bool confirmed; 10 | Animation *sequence; 11 | } AnimationContext; 12 | 13 | typedef struct { 14 | char letter; 15 | LetterStatus status; 16 | AnimationContext animation; 17 | GRect large_rect; 18 | GRect shrunk_rect; 19 | bool confirmed; 20 | } LetterLayerData; 21 | 22 | static GColor s_layer_colors[LetterStatusCount] = { 23 | GColorWhite, 24 | PBL_IF_COLOR_ELSE(GColorLightGray, GColorDarkGray), 25 | PBL_IF_COLOR_ELSE(GColorYellow, GColorWhite), 26 | PBL_IF_COLOR_ELSE(GColorGreen, GColorBlack), 27 | }; 28 | 29 | static GColor s_text_colors[LetterStatusCount] = { 30 | GColorBlack, 31 | GColorBlack, 32 | GColorBlack, 33 | PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite), 34 | }; 35 | 36 | static void prv_update_proc(Layer *layer, GContext *ctx); 37 | static char prv_to_upper(char lower); 38 | static void prv_handle_shrink_finished(Animation *animation, bool finished, void *context); 39 | static void prv_handle_sequence_finished(Animation *animation, bool finished, void *context); 40 | static void prv_fill_rect(Layer *layer, GContext *ctx, GColor color); 41 | 42 | LetterLayer *letter_layer_create(GPoint where) { 43 | Layer *layer = layer_create_with_data(GRect(where.x, where.y, LETTER_LAYER_SIZE, LETTER_LAYER_SIZE), sizeof(LetterLayerData)); 44 | LetterLayerData *data = layer_get_data(layer); 45 | data->status = LetterStatusNeutral; 46 | data->letter = 0; 47 | layer_set_update_proc(layer, prv_update_proc); 48 | return layer; 49 | } 50 | 51 | void letter_layer_set_letter(LetterLayer *layer, char letter, LetterStatus status, bool confirmed, int animated) { 52 | LetterLayerData *data = layer_get_data(layer); 53 | if (animated) { 54 | data->large_rect = layer_get_frame(layer); 55 | data->shrunk_rect = GRect(data->large_rect.origin.x, data->large_rect.origin.y + LETTER_LAYER_SIZE / 2 - 1, LETTER_LAYER_SIZE, 2); 56 | PropertyAnimation *shrink = property_animation_create_layer_frame(layer, &data->large_rect, &data->shrunk_rect); 57 | PropertyAnimation *enlarge = property_animation_create_layer_frame(layer, &data->shrunk_rect, &data->large_rect); 58 | animation_set_duration((Animation *)shrink, 150); 59 | animation_set_duration((Animation *)enlarge, 150); 60 | animation_set_curve((Animation *)shrink, AnimationCurveLinear); 61 | animation_set_curve((Animation *)enlarge, AnimationCurveLinear); 62 | animation_set_delay((Animation *)shrink, (animated - 1) * animation_get_duration((Animation *)shrink, false, false) * 2); 63 | animation_set_handlers((Animation *)shrink, (AnimationHandlers) { .stopped = prv_handle_shrink_finished }, layer); 64 | Animation *sequence = animation_sequence_create((Animation *)shrink, (Animation *)enlarge, NULL); 65 | animation_set_handlers((Animation *)sequence, (AnimationHandlers) { .stopped = prv_handle_sequence_finished }, layer); 66 | data->animation.letter = letter; 67 | data->animation.status = status; 68 | data->animation.confirmed = confirmed; 69 | data->animation.sequence = sequence; 70 | animation_schedule((Animation *)data->animation.sequence); 71 | } else { 72 | data->letter = letter; 73 | data->status = status; 74 | data->confirmed = confirmed; 75 | layer_mark_dirty(layer); 76 | } 77 | } 78 | 79 | static void prv_handle_shrink_finished(Animation *animation, bool finished, void *context) { 80 | LetterLayer *layer = (LetterLayer *)context; 81 | LetterLayerData *data = layer_get_data(layer); 82 | data->letter = data->animation.letter; 83 | data->status = data->animation.status; 84 | data->confirmed = data->animation.confirmed; 85 | } 86 | 87 | static void prv_handle_sequence_finished(Animation *animation, bool finished, void *context) { 88 | LetterLayer *layer = (LetterLayer *)context; 89 | LetterLayerData *data = layer_get_data(layer); 90 | animation_destroy((Animation *)data->animation.sequence); 91 | } 92 | 93 | void letter_layer_destroy(LetterLayer *layer) { 94 | layer_destroy(layer); 95 | } 96 | 97 | static void prv_update_proc(Layer *layer, GContext *ctx) { 98 | LetterLayerData *data = layer_get_data(layer); 99 | 100 | GRect bounds = layer_get_bounds(layer); 101 | graphics_context_set_stroke_color(ctx, GColorBlack); 102 | if (data->confirmed) { 103 | graphics_context_set_fill_color(ctx, s_layer_colors[data->status]); 104 | #ifdef PBL_COLOR 105 | graphics_fill_rect(ctx, GRect(1, 1, bounds.size.w - 2, bounds.size.h - 2), 0, GCornerNone); 106 | #else 107 | if (data->status == LetterStatusWrongPosition) { 108 | graphics_draw_rect(ctx, GRect(1, 1, bounds.size.w - 2, bounds.size.h - 2)); 109 | } else { 110 | graphics_fill_rect(ctx, GRect(1, 1, bounds.size.w - 2, bounds.size.h - 2), 0, GCornerNone); 111 | } 112 | #endif 113 | } else { 114 | #ifdef PBL_COLOR 115 | prv_fill_rect(layer, ctx, s_layer_colors[data->status]); 116 | #endif 117 | } 118 | graphics_draw_rect(ctx, GRect(0, 0, bounds.size.w, bounds.size.h)); 119 | 120 | if (data->letter != 0) { 121 | char letter_string[2] = {prv_to_upper(data->letter), 0}; 122 | graphics_context_set_text_color(ctx, PBL_IF_COLOR_ELSE(true, data->confirmed) ? s_text_colors[data->status] : GColorBlack); 123 | graphics_draw_text(ctx, letter_string, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), GRect(0, -(LETTER_LAYER_SIZE - bounds.size.h)/2 - 1, bounds.size.w, bounds.size.h), GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); 124 | } 125 | } 126 | 127 | static void prv_fill_rect(Layer *layer, GContext *ctx, GColor color) { 128 | GBitmap *fb_bitmap = graphics_capture_frame_buffer_format(ctx, GBitmapFormat8Bit); 129 | uint8_t *fb = gbitmap_get_data(fb_bitmap); 130 | GRect bounds = layer_convert_rect_to_screen(layer, layer_get_bounds(layer)); 131 | int screen_width = gbitmap_get_bytes_per_row(fb_bitmap); 132 | for (int i = bounds.origin.x; i < bounds.origin.x + bounds.size.w; ++i) { 133 | for (int j = bounds.origin.y; j < bounds.origin.y + bounds.size.h; ++j) { 134 | if ((i % 2 == 1 && j % 2 == 1) || (i % 2 == 0 && j % 2 == 0)) { 135 | fb[j * screen_width + i] = color.argb; 136 | } 137 | } 138 | } 139 | graphics_release_frame_buffer(ctx, fb_bitmap); 140 | } 141 | 142 | static char prv_to_upper(char lower) { 143 | return lower - ('a' - 'A'); 144 | } -------------------------------------------------------------------------------- /src/c/stat_window.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "stat_window.h" 3 | #include "stat_tracker.h" 4 | #include "share_layer.h" 5 | #include "distribution_layer.h" 6 | #include "game.h" 7 | 8 | static Window *s_window; 9 | static ScrollLayer *s_scroll_layer; 10 | static ContentIndicator *s_content_indicator; 11 | static Layer *s_up_arrow, *s_down_arrow; 12 | static TextLayer *s_played_label; 13 | static TextLayer *s_played_number; 14 | static char s_played_text[5]; 15 | static TextLayer *s_current_streak_label; 16 | static TextLayer *s_current_streak_number; 17 | static char s_current_streak_text[5]; 18 | static TextLayer *s_max_streak_label; 19 | static TextLayer *s_max_streak_number; 20 | static char s_max_streak_text[5]; 21 | static TextLayer *s_win_percent_label; 22 | static TextLayer *s_win_percent_number; 23 | static char s_win_percent_text[4]; 24 | static ShareLayer *s_share_layer; 25 | static TextLayer *s_share_label; 26 | static DistributionLayer *s_distribution_layer; 27 | static TextLayer *s_distribution_label; 28 | static StatTracker *s_tracker; 29 | 30 | static void prv_window_load(Window *window); 31 | static void prv_window_unload(Window *window); 32 | 33 | void stat_window_push() { 34 | game_restore(); 35 | s_window = window_create(); 36 | window_set_window_handlers(s_window, (WindowHandlers) { 37 | .load = prv_window_load, 38 | .unload = prv_window_unload, 39 | }); 40 | window_stack_push(s_window, true); 41 | } 42 | 43 | void stat_window_pop() { 44 | window_stack_remove(s_window, true); 45 | } 46 | 47 | static TextLayer *prv_create_label(ScrollLayer *scroll_layer, GRect rect, const char *label) { 48 | TextLayer *layer = text_layer_create(rect); 49 | scroll_layer_add_child(scroll_layer, (Layer *)layer); 50 | text_layer_set_font(layer, fonts_get_system_font(FONT_KEY_GOTHIC_18)); 51 | text_layer_set_text_alignment(layer, GTextAlignmentCenter); 52 | text_layer_set_text(layer, label); 53 | text_layer_set_background_color(layer, GColorClear); 54 | return layer; 55 | } 56 | 57 | static TextLayer *prv_create_value(ScrollLayer *scroll_layer, GRect rect) { 58 | TextLayer *layer = text_layer_create(rect); 59 | scroll_layer_add_child(scroll_layer, (Layer *)layer); 60 | text_layer_set_font(layer, fonts_get_system_font(FONT_KEY_BITHAM_34_MEDIUM_NUMBERS)); 61 | text_layer_set_text_alignment(layer, GTextAlignmentCenter); 62 | text_layer_set_background_color(layer, GColorClear); 63 | return layer; 64 | } 65 | 66 | static void prv_window_load(Window *window) { 67 | s_scroll_layer = scroll_layer_create(layer_get_bounds(window_get_root_layer(window))); 68 | layer_add_child(window_get_root_layer(window), (Layer *)s_scroll_layer); 69 | scroll_layer_set_paging(s_scroll_layer, true); 70 | scroll_layer_set_click_config_onto_window(s_scroll_layer, window); 71 | 72 | s_content_indicator = scroll_layer_get_content_indicator(s_scroll_layer); 73 | s_up_arrow = layer_create(GRect(0, 0, 144, 15)); 74 | s_down_arrow = layer_create(GRect(0, 153, 144, 15)); 75 | layer_add_child(window_get_root_layer(window), s_up_arrow); 76 | layer_add_child(window_get_root_layer(window), s_down_arrow); 77 | 78 | const ContentIndicatorConfig up_config = (ContentIndicatorConfig) { 79 | .layer = s_up_arrow, 80 | .times_out = false, 81 | .alignment = GAlignCenter, 82 | .colors = { 83 | .foreground = GColorBlack, 84 | .background = GColorWhite 85 | } 86 | }; 87 | content_indicator_configure_direction(s_content_indicator, ContentIndicatorDirectionUp, &up_config); 88 | 89 | const ContentIndicatorConfig down_config = (ContentIndicatorConfig) { 90 | .layer = s_down_arrow, 91 | .times_out = false, 92 | .alignment = GAlignCenter, 93 | .colors = { 94 | .foreground = GColorBlack, 95 | .background = GColorWhite 96 | } 97 | }; 98 | content_indicator_configure_direction(s_content_indicator, ContentIndicatorDirectionDown, &down_config); 99 | 100 | bool has_completed_game = (game_get_status() == GameStatusWon || game_get_status() == GameStatusLost); 101 | bool show_qr_code = PBL_PLATFORM_TYPE_CURRENT != PlatformTypeAplite && has_completed_game; 102 | 103 | scroll_layer_set_content_size(s_scroll_layer, GSize(144, 168 * (show_qr_code ? 3 : 2))); 104 | 105 | s_played_number = prv_create_value(s_scroll_layer, GRect(0, 5, 72, 44)); 106 | s_played_label = prv_create_label(s_scroll_layer, GRect(0, 39, 72, 25), "Played"); 107 | s_win_percent_number = prv_create_value(s_scroll_layer, GRect(72, 5, 72, 44)); 108 | s_win_percent_label = prv_create_label(s_scroll_layer, GRect(72, 39, 72, 25), "Win %"); 109 | s_current_streak_number = prv_create_value(s_scroll_layer, GRect(0, 69, 72, 44)); 110 | s_current_streak_label = prv_create_label(s_scroll_layer, GRect(0, 103, 72, 55), "Current\nStreak"); 111 | s_max_streak_number = prv_create_value(s_scroll_layer, GRect(72, 69, 72, 44)); 112 | s_max_streak_label = prv_create_label(s_scroll_layer, GRect(72, 103, 72, 55), "Max\nStreak"); 113 | 114 | s_tracker = stat_tracker_load(); 115 | 116 | snprintf(s_played_text, sizeof(s_played_text), "%d", stat_tracker_get_total_played(s_tracker)); 117 | text_layer_set_text(s_played_number, s_played_text); 118 | snprintf(s_win_percent_text, sizeof(s_win_percent_text), "%d", stat_tracker_get_win_percent(s_tracker)); 119 | text_layer_set_text(s_win_percent_number, s_win_percent_text); 120 | snprintf(s_current_streak_text, sizeof(s_current_streak_text), "%d", stat_tracker_get_current_streak(s_tracker)); 121 | text_layer_set_text(s_current_streak_number, s_current_streak_text); 122 | snprintf(s_max_streak_text, sizeof(s_max_streak_text), "%d", stat_tracker_get_max_streak(s_tracker)); 123 | text_layer_set_text(s_max_streak_number, s_max_streak_text); 124 | 125 | s_distribution_layer = distribution_layer_create(GRect(5, 200, 134, 125), s_tracker); 126 | scroll_layer_add_child(s_scroll_layer, s_distribution_layer); 127 | s_distribution_label = prv_create_label(s_scroll_layer, GRect(0, 177, 144, 25), "Distribution"); 128 | 129 | if (show_qr_code) { 130 | s_share_layer = share_layer_create(GRect(0, 354, 144, 125)); 131 | s_share_label = prv_create_label(s_scroll_layer, GRect(0, 479, 144, 25), "Scan to share score"); 132 | LetterStatus statuses[GUESS_LIMIT][WORD_LENGTH]; 133 | memset(statuses, 0, sizeof(statuses)); 134 | game_get_guesses(statuses); 135 | share_layer_set_game_state(s_share_layer, game_get_number(), statuses); 136 | scroll_layer_add_child(s_scroll_layer, s_share_layer); 137 | } 138 | } 139 | 140 | static void prv_window_unload(Window *window) { 141 | text_layer_destroy(s_played_number); 142 | text_layer_destroy(s_played_label); 143 | text_layer_destroy(s_win_percent_number); 144 | text_layer_destroy(s_win_percent_label); 145 | text_layer_destroy(s_current_streak_number); 146 | text_layer_destroy(s_current_streak_label); 147 | text_layer_destroy(s_max_streak_label); 148 | text_layer_destroy(s_max_streak_number); 149 | distribution_layer_destroy(s_distribution_layer); 150 | text_layer_destroy(s_distribution_label); 151 | if (s_share_layer != NULL) { 152 | share_layer_destroy(s_share_layer); 153 | text_layer_destroy(s_share_label); 154 | s_share_layer = NULL; 155 | } 156 | layer_destroy(s_up_arrow); 157 | layer_destroy(s_down_arrow); 158 | stat_tracker_destroy(s_tracker); 159 | scroll_layer_destroy(s_scroll_layer); 160 | } 161 | -------------------------------------------------------------------------------- /src/c/game.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "game.h" 3 | #include "model.h" 4 | #include "word_layer.h" 5 | #include "notify_layer.h" 6 | #include "stat_tracker.h" 7 | #include "stat_window.h" 8 | 9 | typedef struct { 10 | int guess_number; 11 | int current_char; 12 | int word_number; 13 | GameStatus status; 14 | char guesses[GUESS_LIMIT][WORD_LENGTH]; 15 | LetterStatus alphabet_status[26]; 16 | } __attribute__((packed)) GameState; 17 | 18 | char *VICTORY_MESSAGES[GUESS_LIMIT] = {"Genius", "Magnificent", "Impressive", "Splendid", "Great", "Phew"}; 19 | 20 | static Window *s_window; 21 | static WordLayer *s_guess_layers[GUESS_LIMIT]; 22 | static NotifyLayer *s_notify_layer; 23 | static GameState s_game_state; 24 | static bool s_animation_lock; 25 | static bool s_loaded; 26 | static char s_word[WORD_LENGTH]; 27 | 28 | 29 | static void prv_init(); 30 | static void prv_window_load(Window *window); 31 | static void prv_window_unload(Window *window); 32 | static void prv_click_config_provider(void *ctx); 33 | static void prv_handle_select(ClickRecognizerRef recognizer, void *ctx); 34 | static void prv_handle_back(ClickRecognizerRef recognizer, void *ctx); 35 | static void prv_handle_up(ClickRecognizerRef recognizer, void *ctx); 36 | static void prv_handle_down(ClickRecognizerRef recognizer, void *ctx); 37 | static void prv_cycle_letter(int direction); 38 | static void prv_update_alphabet_status(char letter, LetterStatus status); 39 | static void prv_notify(char* message); 40 | static void prv_animation_complete(void *context); 41 | static void prv_save_state(); 42 | static void prv_restore_state(); 43 | static void prv_record_result(); 44 | 45 | void game_restore() { 46 | prv_restore_state(); 47 | } 48 | 49 | void show_game() { 50 | prv_init(); 51 | } 52 | 53 | int game_get_number() { 54 | return s_game_state.word_number; 55 | } 56 | 57 | int game_get_guess_number() { 58 | return s_game_state.guess_number; 59 | } 60 | 61 | GameStatus game_get_status() { 62 | return s_game_state.status; 63 | } 64 | 65 | int game_get_guesses(LetterStatus guesses[GUESS_LIMIT][WORD_LENGTH]) { 66 | GameState *s = &s_game_state; 67 | for (int i = 0; i <= s->guess_number; ++i) { 68 | score_word(s->guesses[i], s_word, guesses[i]); 69 | } 70 | return s->guess_number+1; 71 | } 72 | 73 | static void prv_init() { 74 | s_window = window_create(); 75 | window_set_window_handlers(s_window, (WindowHandlers) { 76 | .load = prv_window_load, 77 | .unload = prv_window_unload, 78 | }); 79 | window_set_click_config_provider(s_window, prv_click_config_provider); 80 | window_stack_push(s_window, true); 81 | } 82 | 83 | static void prv_window_load(Window *window) { 84 | for (int i = 0; i < GUESS_LIMIT; ++i) { 85 | s_guess_layers[i] = word_layer_create(GPoint(7, 6 + i * (WORD_LAYER_HEIGHT + 1))); 86 | layer_add_child(window_get_root_layer(window), s_guess_layers[i]); 87 | } 88 | prv_restore_state(); 89 | GameState *s = &s_game_state; 90 | for (int i = 0; i < s->guess_number; ++i) { 91 | for (int j = 0; j < WORD_LENGTH; ++j) { 92 | word_layer_set_letter(s_guess_layers[i], j, s->guesses[i][j], LetterStatusNeutral, false); 93 | } 94 | LetterStatus statuses[WORD_LENGTH]; 95 | score_word(s->guesses[i], s_word, statuses); 96 | word_layer_apply_score(s_guess_layers[i], statuses, false); 97 | } 98 | for (int j = 0; j < WORD_LENGTH; ++j) { 99 | if (s->guesses[s->guess_number][j] == 0) { 100 | break; 101 | } 102 | word_layer_set_letter(s_guess_layers[s->guess_number], j, s->guesses[s->guess_number][j], s->alphabet_status[s->guesses[s->guess_number][j] - 'a'], s->status != GameStatusPlaying); 103 | } 104 | if (s->status == GameStatusPlaying) { 105 | word_layer_set_cursor(s_guess_layers[s_game_state.guess_number], s_game_state.current_char); 106 | } 107 | } 108 | 109 | static void prv_window_unload(Window *window) { 110 | prv_save_state(); 111 | for (int i = 0; i < GUESS_LIMIT; ++i) { 112 | word_layer_destroy(s_guess_layers[i]); 113 | } 114 | if (s_notify_layer != NULL) { 115 | notify_layer_destroy(s_notify_layer); 116 | s_notify_layer = NULL; 117 | s_animation_lock = false; 118 | } 119 | } 120 | 121 | static void prv_save_state() { 122 | APP_LOG(APP_LOG_LEVEL_INFO, "prv_save_state()"); 123 | persist_write_int(1, 1); 124 | persist_write_data(2, &s_game_state, sizeof(GameState)); 125 | } 126 | 127 | static void prv_restore_state() { 128 | APP_LOG(APP_LOG_LEVEL_INFO, "prv_restore_state()"); 129 | if (s_loaded) { 130 | APP_LOG(APP_LOG_LEVEL_INFO, "already loaded state, bailing"); 131 | return; 132 | } 133 | GameState *s = &s_game_state; 134 | word_of_the_day(s_word); 135 | int word_number = wordle_number(); 136 | if (persist_read_int(1) == 1) { 137 | APP_LOG(APP_LOG_LEVEL_INFO, "usable state shuold be present, loading..."); 138 | persist_read_data(2, s, sizeof(GameState)); 139 | if (s->word_number == word_number) { 140 | APP_LOG(APP_LOG_LEVEL_INFO, "using loaded state"); 141 | s_loaded = true; 142 | return; 143 | } 144 | } 145 | APP_LOG(APP_LOG_LEVEL_INFO, "trashing loaded state"); 146 | memset(s, 0, sizeof(GameState)); 147 | s->word_number = word_number; 148 | s_loaded = true; 149 | } 150 | 151 | static void prv_click_config_provider(void *ctx) { 152 | window_single_click_subscribe(BUTTON_ID_SELECT, prv_handle_select); 153 | window_single_click_subscribe(BUTTON_ID_BACK, prv_handle_back); 154 | window_single_repeating_click_subscribe(BUTTON_ID_UP, 100, prv_handle_up); 155 | window_single_repeating_click_subscribe(BUTTON_ID_DOWN, 100, prv_handle_down); 156 | } 157 | 158 | static void prv_handle_up(ClickRecognizerRef recognizer, void *ctx) { 159 | if (s_animation_lock) { 160 | return; 161 | } 162 | if (s_notify_layer != NULL) { 163 | prv_notify(NULL); 164 | return; 165 | } 166 | prv_cycle_letter(1); 167 | } 168 | 169 | static void prv_handle_down(ClickRecognizerRef recognizer, void *ctx) { 170 | if (s_animation_lock) { 171 | return; 172 | } 173 | if (s_notify_layer != NULL) { 174 | prv_notify(NULL); 175 | return; 176 | } 177 | prv_cycle_letter(-1); 178 | } 179 | 180 | static void prv_cycle_letter(int direction) { 181 | GameState *s = &s_game_state; 182 | if (s->status != GameStatusPlaying) { 183 | return; 184 | } 185 | char c = s->guesses[s->guess_number][s->current_char]; 186 | if (c == 0) { 187 | c = direction > 0 ? 'a' : 'z'; 188 | } else { 189 | c += direction; 190 | if (c > 'z') c = 'a'; 191 | else if (c < 'a') c = 'z'; 192 | } 193 | s->guesses[s->guess_number][s->current_char] = c; 194 | word_layer_set_letter(s_guess_layers[s->guess_number], s->current_char, c, s->alphabet_status[c - 'a'], false); 195 | } 196 | 197 | static void prv_handle_select(ClickRecognizerRef recognizer, void *ctx) { 198 | GameState *s = &s_game_state; 199 | if (s_animation_lock) { 200 | return; 201 | } 202 | if (s->status != GameStatusPlaying) { 203 | stat_window_push(); 204 | window_stack_remove(s_window, true); 205 | return; 206 | } 207 | if (s_notify_layer != NULL) { 208 | prv_notify(NULL); 209 | return; 210 | } 211 | if (s->guesses[s->guess_number][s->current_char] == 0) { 212 | if (s->guess_number > 0) { 213 | char c = s->guesses[s->guess_number-1][s->current_char]; 214 | s->guesses[s->guess_number][s->current_char] = c; 215 | word_layer_set_letter(s_guess_layers[s->guess_number], s->current_char, c, s->alphabet_status[c - 'a'], false); 216 | } else { 217 | vibes_short_pulse(); 218 | } 219 | return; 220 | } 221 | if (s->current_char < WORD_LENGTH - 1) { 222 | ++s->current_char; 223 | word_layer_set_cursor(s_guess_layers[s->guess_number], s->current_char); 224 | return; 225 | } 226 | if (!is_valid_word(s->guesses[s->guess_number])) { 227 | char message[28] = "'xxxxx' is not a valid word"; 228 | memcpy(message + 1, s->guesses[s->guess_number], 5); 229 | prv_notify(message); 230 | vibes_short_pulse(); 231 | return; 232 | } 233 | APP_LOG(APP_LOG_LEVEL_INFO, "Scoring word..."); 234 | LetterStatus statuses[WORD_LENGTH]; 235 | bool correct = score_word(s->guesses[s->guess_number], s_word, statuses); 236 | for (int i = 0; i < WORD_LENGTH; ++i) { 237 | prv_update_alphabet_status(s->guesses[s->guess_number][i], statuses[i]); 238 | } 239 | APP_LOG(APP_LOG_LEVEL_INFO, "Word was correct: %d", correct); 240 | APP_LOG(APP_LOG_LEVEL_INFO, "Statuses: %d%d%d%d%d", statuses[0], statuses[1], statuses[2], statuses[3], statuses[4]); 241 | word_layer_apply_score(s_guess_layers[s->guess_number], statuses, true); 242 | word_layer_set_cursor(s_guess_layers[s->guess_number], -1); 243 | if (correct) { 244 | s->status = GameStatusWon; 245 | prv_record_result(); 246 | } else { 247 | if (s->guess_number >= GUESS_LIMIT - 1) { 248 | s->status = GameStatusLost; 249 | prv_record_result(); 250 | } else { 251 | ++s->guess_number; 252 | s->current_char = 0; 253 | } 254 | } 255 | s_animation_lock = true; 256 | app_timer_register(1500, prv_animation_complete, NULL); 257 | } 258 | 259 | static void prv_record_result() { 260 | APP_LOG(APP_LOG_LEVEL_INFO, "recording result"); 261 | StatTracker *tracker = stat_tracker_load(); 262 | int result = 0; 263 | 264 | if (s_game_state.status == GameStatusWon) { 265 | result = s_game_state.guess_number + 1; 266 | } 267 | stat_tracker_record_result(tracker, s_game_state.word_number, result); 268 | stat_tracker_destroy(tracker); 269 | } 270 | 271 | static void prv_animation_complete(void *context) { 272 | GameState *s = &s_game_state; 273 | s_animation_lock = false; 274 | if (s->status == GameStatusWon) { 275 | prv_notify(VICTORY_MESSAGES[s->guess_number]); 276 | } else if (s->status == GameStatusLost) { 277 | char message[] = "'XXXXX'"; 278 | memcpy(message + 1, s_word, 5); 279 | prv_notify(message); 280 | } else { 281 | word_layer_set_cursor(s_guess_layers[s->guess_number], 0); 282 | } 283 | } 284 | 285 | static void prv_handle_back(ClickRecognizerRef recognizer, void *ctx) { 286 | if (s_animation_lock) { 287 | return; 288 | } 289 | GameState *s = &s_game_state; 290 | if (s->status != GameStatusPlaying) { 291 | window_stack_pop(true); 292 | return; 293 | } 294 | if (s_notify_layer != NULL) { 295 | prv_notify(NULL); 296 | return; 297 | } 298 | if (s->current_char > 0) { 299 | --s->current_char; 300 | word_layer_set_cursor(s_guess_layers[s->guess_number], s->current_char); 301 | } else { 302 | window_stack_pop(true); 303 | } 304 | } 305 | 306 | static void prv_update_alphabet_status(char letter, LetterStatus status) { 307 | int index = letter - 'a'; 308 | if (status > s_game_state.alphabet_status[index]) { 309 | s_game_state.alphabet_status[index] = status; 310 | } 311 | } 312 | 313 | static void prv_notify(char *message) { 314 | if (s_notify_layer != NULL) { 315 | layer_remove_from_parent(s_notify_layer); 316 | notify_layer_destroy(s_notify_layer); 317 | s_notify_layer = NULL; 318 | } 319 | if (message == NULL) { 320 | return; 321 | } 322 | s_notify_layer = notify_layer_create(message); 323 | layer_add_child(window_get_root_layer(s_window), s_notify_layer); 324 | } 325 | -------------------------------------------------------------------------------- /resources/solutions.dat: -------------------------------------------------------------------------------- 1 | cigarrebutsissyhumphawakeblushfocalevadenavalserveheathdwarfmodelkarmastinkgradequietbenchabatefeignmajordeathfreshcruststoolcolonabasemarryreactbattyprideflosshelixcroakstaffpaperunfedwhelptrawloutdoadobecrazysowerrepaydigitcratecluckspikemimicpoundmaximlinenunmetfleshboobyforthfirststandbellyivoryseedyprintyearndrainbribestoutpanelcrassflumeoffalagreeerrorswirlarguebleeddeltaflicktotemwooerfrontshrubparrybiomelapelstartgreetgonergolemlustyloopyroundauditlyinggammalaborisletcivicforgecornymoultbasicsaladagatespicysprayessayfjordspendkebabguildabackmotoralonehatchhyperthumbdowryoughtbelchdutchpilottweedcometjauntenemasteedabyssgrowlflingdozenboozyerodeworldgougeclickbriargreataltarpulpyblurtcoastduchygroinfixergrouproguebadlysmartpithygaudychillheronvodkafinersurerradiorougeperchretchwroteclocktildestoreprovebringsolvecheatgrimeexultusherepochtriadbreakrhinoviralconicmassesonicvitaltraceusingpeachchampbatonbrakepluckcrazegripewearypickyacuteferryasidetapirtrollunifyrebusboosttrusssiegetigerbanalslumpcrankgorgequerydrinkfavorabbeytangypanicsolarshireproxypointrobotprickwincecrimpknollsugarwhackmountperkycouldwrunglightthosemoistshardpleataloftskillelderframehumorpauseulcerultrarobincynicaromacaulkshakedodgeswilltacitotherthorntroveblokevividspillchantchokerupeenastymournaheadbrineclothhoardsweetmonthlapsewatchtodayfocussmeltteasecatermoviesauteallowrenewtheirsloshpurgechestdepotepoxynymphfoundshallstovelowlysnouttropefewershawlnatalcommaforayscarestairblacksquadroyalchunkminceshamecheekampleflairfoyercargooxideplantoliveinertaskewheistshownzestytrashlarvaforgostoryhairytrainhomerbadgemidstcannyshinegeckofarceslungtipsymetalyielddelvebeingscourglassgamerscrapmoneyhingealbumvouchassettiaracreptbayouatollmanorcreakshowyphasefrothdepthgloomfloodtraitgirthpietygoosefloatdonoratoneprimoapronblowncacaoloserinputgloatawfulbrinksmitebeadyrustyretrodrollgawkyhutchpintoegretlilacseverfieldfluffagapevoicesteadberthmadamnightblandliverwedgeroomywackyflockangrytriteaphidtrystmidgepowerelopecinchmottostompupsetbluffcrampquartcoylyyouthrhymebuggyaliensmearunfitpattyclinggleanlabelhunkykhakipokergrueltwicetwangshrugtreatwastemeritwovenneedyclownironyrudergauzechiefonsetprizefungicharmgullyinterwhooptauntleeryclassthemeloftytibiaboozealphathymedoubtparerchutesticktricealikerecapsaintglorygrateadmitbrisksoggyusurpscaldscornleavetwinestingboughmarshslothdandyvigorhowdyenjoyvalidionicequalfloorcatchspadesteinexistquirkdenimgrovespielmummyfaultfoggyfloutcarrysneaklibelwaltzaptlypineyineptaloudphotodreamstaleunitesnarlbakerthereglyphpoochhippyspellfollylousegulchvaultgodlythrewfleetgraveinaneshockcravespitevalveskimpclaimrainymustypiquedaddyquasiariseagingvaletopiumavertstuckrecutmulchgenreplumeriflecountincurtotalwrestmochadeterstudyloversaferrivetfunnysmokemoundunduesedanpaganswineguilegustyequiptoughcanoechaoscovethumanudderlunchblaststraymangameleeleftyquickpastegivenoctetrisengroanleakygrindcarveloosesadlyspiltappleslackhoneyfinalsheeneeriemintyslickderbywharfspeltcoacheruptsingepricespawnfairyjiffyfilmystackchosesleepardornannyniecewoozyhandygracedittostankcreamusualdiodevalorangleninjamuddychasereplypronespoilheartshadedinerarsononionsleetdowelcouchpalsybowelsmileevokecreeklanceeagleidiotsirenbuiltembedawarddrossannulgoodyfrownpatioladenhumidelitelymphedifymightresetvisitgustopursevaporcrockwritesunnyloathchaffslidequeervenomstampsorrystillacornapingpushytamerhatermaniaawokebrawnswiftexilebirchluckyfreerriskyghostplierlunarwinchsnarenursehouseboraxnicerlurchexaltaboutsavvytoxintunicpriedinlaychumplankycresseatereludecyclekittyboulemorontenetplacelobbyplushvigilindexblinkclungqualmcroupclinkjuicystagedecaynervefliershaftcrookcleanchinaridgevowelgnomesnuckicingspinyrigorsnailflownrabidprosethankpoppybudgefibermoldydowdykneeltrackcaddyquelldumpypalersworerebarscubasplatflyerhornymasondoingozoneamplymolarovarybesetqueuecliffmagictrucesportfritzedicttwirlversellamaeatenrangewhiskhovelrehabmacawsigmaspoutvervesushidyingfetidbrainbuddythumpscioncandychordbasinmarchcrowdarborgaylymuskystaindallyblessbravostungtitlerulerkioskblondennuilayerfluidtattyscorecutiezebrabargemateyblueraidershookriverprivybetelfriskbongobegunazureweavegeniesoundglovebraidscopewrylyroverassayoceanbloomiratelaterwokensilkywreckdweltslatesmacksolidamazehazelwristjollyglobeflintrousecivilvistarelaxcoveralivebeechjettyblissvocaloftendollyeightjokersinceeventensueshuntdiverposerworstsweepalleycreedanimeleafybosomduncestarepudgywaivechoirstoodspokeoutgodelaybilgeidealclaspseizehotlylaughsieveblockmeantgrapenoosehardyshieddrawldaisyputtystrutburnttulipcrickidyllvixenfurorgeekycoughnaiveshoalstorkbatheauntycheckprimebrassouterfurryrazorelectevictimplydemurquotahavencavilswearcrumpdoughgavelwagonsalonnudgeharempitchswornpupilexcelstonycabinunzipqueentroutpolypearthstormuntiltaperenterchildadoptminorfattyhuskybravefiletslimeglinttreadstealregalguesteverymurkysharesporehoistbuxominnerotterdimlylevelsumacdonutstiltarenasheetscrubfancyslimypearlsillyporchdingosepiaambleshadybreadfriarreigndairyquillcrossbroodtubershearpositblankvillashankpiggyfreakwhichamongfecalshellwouldalgaelargerabbiagonyamusebushycopseswoonknifepouchascotplanecrownurbansniderelayabideviolarajahstrawdillycrashamassthirdtricktutorwoodyblurbgriefdiscowheresassybeachsaunacomiccluedcreepcastegrazesnufffrockgonaddrunkprongluridsteelhalvebuyervinylutilesmelladageworrytastylocaltradefinchashenmodalgauntcloveenactadornroastspecksheikmissygruntsnooppartytouchmafiaemceearraysouthvapidjellyskulkangsttuballowercrestsweatcyberadoretardyswaminotchgroomroachhitchyoungalignreadyfrondstrappureerealmvenueswarmoffersevendryerdiarydrylydrankacridheadythetajuntopixiequothbonusshaltpenneamenddatumbuildpianoshelflodgesuingrearmcoralramenworthpsalminferovertmayorovoidglideusagepoiserandychuckprankfishytoothetherdroveidlerswathstintwhilebegatapplyslangtarotradarcredoawarecanonshifttimerbylawserumthreesteakiliacshirkbluntpuppypenaljoistbunnyshapebegetwheeladeptstuntstoletopazchoreflukeafootbloatbullydensecapersneerboxerjumbolungespaceavailshortslurployalflirtpizzaconchtempodroopplatebibleplunkafoulsavoysteepagilestakedwellknavebeardarosemotifsmashbroilglareshovebaggymammyswampalongrugbywagerquacksquatsnakydebitmangeskateninthjousttrampspurnmedalmicrorebelflanklearnnadirmaplecomfyremitgruffesterleastmogulfetchcauseoakenaglowmeatygaffeshylyracerprowlthiefsternpoesyrockytweetwaistspiregropehavocpatsytrulyfortydeityuncleswishgiverpreenbevellemurdraftslopeannoylingobleakdittycurlycedardirgegrownhordedroolshuckcryptcuminstockgravylocuswiderbreedquitechafecacheblimpdeignfiendlogiccheapeliderigidfalserenalpencerowdyshootblazeenvoypossebriefneverabortmousemuckysulkyfierymediatrunkyeastclearskunkscalpbittyciderkoaladuvetseguecremesupergrillafterowneremberreachnoblyemptyspeedgipsyrecursmockdreadmergeburstkappaamityshakyhovercarolsnortsynodfainthauntflourchairdetoxshrewtensepliedquarkburlynovelwaxenstoicjerkyblitzbeefylyrichussytowelquiltbelowbingowispybrashsconetoasteaselsaucyvaluespicehonorroutesharpbawdyradiiskullphonyissuelagerswellurinegassytrialfloraupperlatchwightbrickretryhollydecalgrassshackdogmamoverdefersoberopticcriervyingnomadflutehipposharkdrierobesebugletawnychalkfeastruddypedalscarfcruelbleattidalslushsemenwindydustysallyigloonerdyjewelshonewhalehymenabusefugueelbowcrumbpansywelshsyruptersesuavegamutswungdrakefreedafireshirtgroutoddlytitheplaiddummybroomblindtorchenemyagaintyingpeskyaltergazernobleethosbrideextoldecorhobbybeastidiomutterthesesixthalarmeraseelegyspunkpiperscalyscoldheftychicksootycanalwhinyslashquakejointsweptprudeheavywieldfemmelassomaizeshalescrewspreesmokywhiffscentgladespentprismstokeriperorbitcocoaguilthumusshushtablesmirkwrongnoisyalertshinyelateresinwholehunchpixelpolarhotelswordcleatmangorumbapuffyfillybillyleashcloutdanceovatefacetchilipaintlinercuriosaltyaudiosnakefablecloaknavelspurtpestobalmyflashunwedearlychurnweedystumpleasewittywimpyspoofsanerblendsalsathickwartymanicblaresquibspoonprobecrepeknackforcedebutorderhasteteethagentwidenicilysliceingotclashjurorbloodabodethrowunitypivotslepttroopsparesewerparsemorphcactitackyspooldemonmoodyannexbeginfuzzypatchwaterlumpyadminomegalimittabbymachoaisleskiffbasisplankvergebotchcrawllousyslaincubicraisewrackguidefoistcameounderactorrevuefraudharpyscoopclimbreferoldenclerkdebartallyethiccairntulleghoulhillycrudeapartscaleolderplainspermbrinyabbotrerunquestcrispboundbefitdrawnsuiteitchycheerbagelguessbroadaxiomchardcaputleantharshcurseproudswingopinetastelupusgumbominergreenchasmlipidtopicarmorbrushcranemuralabledhabitbossymakerduskydizzylithebrookjazzyfiftysensegiantsurlylegalfatalflunkbeganprunesmallslantscofftorusninnycoveyvipertakenmoralvogueowingtokenentryboothvoterchideelfinebonyneighminimmelonkneeddecoyvoilaanklearrowmushytribeceaseeagerbirthgraphodderterraweirdtriedclackcolorroughweighuncutladlestripcraftminusdiceytitanlucidvicardressditchgypsypastataffyflameswoopaloofsightbroketearychartsixtywordysheerlepernoseybulgesavorclampfunkyfoamytoxicbrandplumbdingybuttedrilltripebiceptenorkrillworsedramahyenathinkratiocobrabasilscrumbusedphonecourtcamelproofheardangelpetalpoutythrobmaybefetalsprigspineshoutcadetmacrododgysatyrrarerbingetrendnuttyleaptamisssplitmyrrhwidthsonartowerbaronfeverwaversparkbeliesloopexpelsmotebalerabovenorthwaferscantfrillawashsnackscowlfraildriftlimbofencemotelouncewreakreveltalonpriorkneltcelloflakedebuganodecrimesalvescoutimbuepinkystavevaguechockfightvideostoneteachcleftfrostprawnbootytwistapneastiffplazaledgetweakboardgrantmedicbaconcablebrawlslunkraspyforumdronewomenmucusboasttoddycoventumortruerwrathstallsteamaxialpurerdailytrailnichemealyjuicenylonplumpmerryflailpapalwheatberrycowererectbruteleggysnipesinewskierpennyjumpyrallyumbrascarymodemgrossaviangreedsatintonicparkasnifflividstarktrumpgiddyreusetabooavoidquotedevillikenglossgayerberetnoiseglanddealtslingrumoroperathightongaflarewoundwhitebulkyetudehorsecircapaddyinboxfizzygrainexertsurgegleambellesalvocrushfruitsappytakertractovinespikyfrankreedyfilthspasmheavemamborightclanktrustlumenbornespooksauceamberlathecaratcorerdirtyslylyaffixalloytaintsheepkinkywoolymauveflungyachtfriedquailbruntgrimycurvycageyrinsedeucestategraspmilkybisongraftsandybasteflaskhedgegirlyswashboneycoupeendowabhorwelchbladetightgeesemisermirthcloudcaballeechclosetenthpecandroitgrailcloneguiseralphtangobiddysmithmowerpayeeserifdrapefifthspankglazeallottruckkayakvirustestytepeefullyzonalmetrocurrygrandbanjoaxionbezeloccurchainnasalgooeyfilerbraceallaypubicravenpleadgnashflakymunchdullyekingthingslinkhurrytheftshornpygmyranchwringlemonshoremammafrozenewerstylemooseanticdrownveganchessguppyunionleverlorryimagecabbydruidexacttruthdopeyspearcriedchimecronystunktimidbatchgaugerotorcrackcurvelattewitchbunchrepelanvilsoapymeterbrothmadlydriedsceneknownmagmaroostwomanthongpunchpastydownykneadwhirlrapidclangangerdrivegoofyemailmusicstuffbleepridermeccafoliosetupversoquashfaunagummyhappynewlyfussyrelicguavarattyfudgefemurchirpfortealibiwhinepettygollyplaitfleckfelongourdbrownthrumficusstashdecrywiserjuntavisordauntscreeimpelawaitpresswhoseturbostoopspeakmangyeyinginletcronepulsemossystaidhencepinchteddysullysnoreripensnowyatticgoingleachmouthhoundclumptonalbigotperilpieceblamehautespiedundidintrobasalrodeoguardsteerloamyscampscrammanlyhellovauntorganferalknockextracondoadaptwillypolkarayonskirtfaithtorsomatchmercytepidsleekrisertwixtpeaceflushcattyloginejectrogerrivaluntierefitaortaadultjudgerowerartsyruralshavebobbyeclatfellagailyharryhastyhydroliegeoctalombrepayersoothunsetunlitvomitfannyfetusbutchstalkflackwidowaugur -------------------------------------------------------------------------------- /src/c/vendor/qrcodegen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QR Code generator library (C) 3 | * 4 | * Copyright (c) Project Nayuki. (MIT License) 5 | * https://www.nayuki.io/page/qr-code-generator-library 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | * this software and associated documentation files (the "Software"), to deal in 9 | * the Software without restriction, including without limitation the rights to 10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | * the Software, and to permit persons to whom the Software is furnished to do so, 12 | * subject to the following conditions: 13 | * - The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * - The Software is provided "as is", without warranty of any kind, express or 16 | * implied, including but not limited to the warranties of merchantability, 17 | * fitness for a particular purpose and noninfringement. In no event shall the 18 | * authors or copyright holders be liable for any claim, damages or other 19 | * liability, whether in an action of contract, tort or otherwise, arising from, 20 | * out of or in connection with the Software or the use or other dealings in the 21 | * Software. 22 | */ 23 | 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | 36 | /* 37 | * This library creates QR Code symbols, which is a type of two-dimension barcode. 38 | * Invented by Denso Wave and described in the ISO/IEC 18004 standard. 39 | * A QR Code structure is an immutable square grid of dark and light cells. 40 | * The library provides functions to create a QR Code from text or binary data. 41 | * The library covers the QR Code Model 2 specification, supporting all versions (sizes) 42 | * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. 43 | * 44 | * Ways to create a QR Code object: 45 | * - High level: Take the payload data and call qrcodegen_encodeText() or qrcodegen_encodeBinary(). 46 | * - Low level: Custom-make the list of segments and call 47 | * qrcodegen_encodeSegments() or qrcodegen_encodeSegmentsAdvanced(). 48 | * (Note that all ways require supplying the desired error correction level and various byte buffers.) 49 | */ 50 | 51 | 52 | /*---- Enum and struct types----*/ 53 | 54 | /* 55 | * The error correction level in a QR Code symbol. 56 | */ 57 | enum qrcodegen_Ecc { 58 | // Must be declared in ascending order of error protection 59 | // so that an internal qrcodegen function works properly 60 | qrcodegen_Ecc_LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords 61 | qrcodegen_Ecc_MEDIUM , // The QR Code can tolerate about 15% erroneous codewords 62 | qrcodegen_Ecc_QUARTILE, // The QR Code can tolerate about 25% erroneous codewords 63 | qrcodegen_Ecc_HIGH , // The QR Code can tolerate about 30% erroneous codewords 64 | }; 65 | 66 | 67 | /* 68 | * The mask pattern used in a QR Code symbol. 69 | */ 70 | enum qrcodegen_Mask { 71 | // A special value to tell the QR Code encoder to 72 | // automatically select an appropriate mask pattern 73 | qrcodegen_Mask_AUTO = -1, 74 | // The eight actual mask patterns 75 | qrcodegen_Mask_0 = 0, 76 | qrcodegen_Mask_1, 77 | qrcodegen_Mask_2, 78 | qrcodegen_Mask_3, 79 | qrcodegen_Mask_4, 80 | qrcodegen_Mask_5, 81 | qrcodegen_Mask_6, 82 | qrcodegen_Mask_7, 83 | }; 84 | 85 | 86 | /* 87 | * Describes how a segment's data bits are interpreted. 88 | */ 89 | enum qrcodegen_Mode { 90 | qrcodegen_Mode_NUMERIC = 0x1, 91 | qrcodegen_Mode_ALPHANUMERIC = 0x2, 92 | qrcodegen_Mode_BYTE = 0x4, 93 | qrcodegen_Mode_KANJI = 0x8, 94 | qrcodegen_Mode_ECI = 0x7, 95 | }; 96 | 97 | 98 | /* 99 | * A segment of character/binary/control data in a QR Code symbol. 100 | * The mid-level way to create a segment is to take the payload data 101 | * and call a factory function such as qrcodegen_makeNumeric(). 102 | * The low-level way to create a segment is to custom-make the bit buffer 103 | * and initialize a qrcodegen_Segment struct with appropriate values. 104 | * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. 105 | * Any segment longer than this is meaningless for the purpose of generating QR Codes. 106 | * Moreover, the maximum allowed bit length is 32767 because 107 | * the largest QR Code (version 40) has 31329 modules. 108 | */ 109 | struct qrcodegen_Segment { 110 | // The mode indicator of this segment. 111 | enum qrcodegen_Mode mode; 112 | 113 | // The length of this segment's unencoded data. Measured in characters for 114 | // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. 115 | // Always zero or positive. Not the same as the data's bit length. 116 | int numChars; 117 | 118 | // The data bits of this segment, packed in bitwise big endian. 119 | // Can be null if the bit length is zero. 120 | uint8_t *data; 121 | 122 | // The number of valid data bits used in the buffer. Requires 123 | // 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8. 124 | // The character count (numChars) must agree with the mode and the bit buffer length. 125 | int bitLength; 126 | }; 127 | 128 | 129 | 130 | /*---- Macro constants and functions ----*/ 131 | 132 | #define qrcodegen_VERSION_MIN 1 // The minimum version number supported in the QR Code Model 2 standard 133 | #define qrcodegen_VERSION_MAX 2 // The maximum version number supported in the QR Code Model 2 standard 134 | 135 | // Calculates the number of bytes needed to store any QR Code up to and including the given version number, 136 | // as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];' 137 | // can store any single QR Code from version 1 to 25 (inclusive). The result fits in an int (or int16). 138 | // Requires qrcodegen_VERSION_MIN <= n <= qrcodegen_VERSION_MAX. 139 | #define qrcodegen_BUFFER_LEN_FOR_VERSION(n) ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) / 8 + 1) 140 | 141 | // The worst-case number of bytes needed to store one QR Code, up to and including 142 | // version 40. This value equals 3918, which is just under 4 kilobytes. 143 | // Use this more convenient value to avoid calculating tighter memory bounds for buffers. 144 | #define qrcodegen_BUFFER_LEN_MAX qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX) 145 | 146 | 147 | 148 | /*---- Functions (high level) to generate QR Codes ----*/ 149 | 150 | /* 151 | * Encodes the given text string to a QR Code, returning true if successful. 152 | * If the data is too long to fit in any version in the given range 153 | * at the given ECC level, then false is returned. 154 | * 155 | * The input text must be encoded in UTF-8 and contain no NULs. 156 | * Requires 1 <= minVersion <= maxVersion <= 40. 157 | * 158 | * The smallest possible QR Code version within the given range is automatically 159 | * chosen for the output. Iff boostEcl is true, then the ECC level of the result 160 | * may be higher than the ecl argument if it can be done without increasing the 161 | * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or 162 | * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow). 163 | * 164 | * About the arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion): 165 | * - Before calling the function: 166 | * - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow 167 | * reading and writing; hence each array must have a length of at least len. 168 | * - The two ranges must not overlap (aliasing). 169 | * - The initial state of both ranges can be uninitialized 170 | * because the function always writes before reading. 171 | * - After the function returns: 172 | * - Both ranges have no guarantee on which elements are initialized and what values are stored. 173 | * - tempBuffer contains no useful data and should be treated as entirely uninitialized. 174 | * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). 175 | * 176 | * If successful, the resulting QR Code may use numeric, 177 | * alphanumeric, or byte mode to encode the text. 178 | * 179 | * In the most optimistic case, a QR Code at version 40 with low ECC 180 | * can hold any UTF-8 string up to 2953 bytes, or any alphanumeric string 181 | * up to 4296 characters, or any digit string up to 7089 characters. 182 | * These numbers represent the hard upper limit of the QR Code standard. 183 | * 184 | * Please consult the QR Code specification for information on 185 | * data capacities per version, ECC level, and text encoding mode. 186 | */ 187 | bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], 188 | enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); 189 | 190 | 191 | /* 192 | * Encodes the given binary data to a QR Code, returning true if successful. 193 | * If the data is too long to fit in any version in the given range 194 | * at the given ECC level, then false is returned. 195 | * 196 | * Requires 1 <= minVersion <= maxVersion <= 40. 197 | * 198 | * The smallest possible QR Code version within the given range is automatically 199 | * chosen for the output. Iff boostEcl is true, then the ECC level of the result 200 | * may be higher than the ecl argument if it can be done without increasing the 201 | * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or 202 | * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow). 203 | * 204 | * About the arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion): 205 | * - Before calling the function: 206 | * - The array ranges dataAndTemp[0 : len] and qrcode[0 : len] must allow 207 | * reading and writing; hence each array must have a length of at least len. 208 | * - The two ranges must not overlap (aliasing). 209 | * - The input array range dataAndTemp[0 : dataLen] should normally be 210 | * valid UTF-8 text, but is not required by the QR Code standard. 211 | * - The initial state of dataAndTemp[dataLen : len] and qrcode[0 : len] 212 | * can be uninitialized because the function always writes before reading. 213 | * - After the function returns: 214 | * - Both ranges have no guarantee on which elements are initialized and what values are stored. 215 | * - dataAndTemp contains no useful data and should be treated as entirely uninitialized. 216 | * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). 217 | * 218 | * If successful, the resulting QR Code will use byte mode to encode the data. 219 | * 220 | * In the most optimistic case, a QR Code at version 40 with low ECC can hold any byte 221 | * sequence up to length 2953. This is the hard upper limit of the QR Code standard. 222 | * 223 | * Please consult the QR Code specification for information on 224 | * data capacities per version, ECC level, and text encoding mode. 225 | */ 226 | bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], 227 | enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); 228 | 229 | 230 | /*---- Functions (low level) to generate QR Codes ----*/ 231 | 232 | /* 233 | * Encodes the given segments to a QR Code, returning true if successful. 234 | * If the data is too long to fit in any version at the given ECC level, 235 | * then false is returned. 236 | * 237 | * The smallest possible QR Code version is automatically chosen for 238 | * the output. The ECC level of the result may be higher than the 239 | * ecl argument if it can be done without increasing the version. 240 | * 241 | * About the byte arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX): 242 | * - Before calling the function: 243 | * - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow 244 | * reading and writing; hence each array must have a length of at least len. 245 | * - The two ranges must not overlap (aliasing). 246 | * - The initial state of both ranges can be uninitialized 247 | * because the function always writes before reading. 248 | * - The input array segs can contain segments whose data buffers overlap with tempBuffer. 249 | * - After the function returns: 250 | * - Both ranges have no guarantee on which elements are initialized and what values are stored. 251 | * - tempBuffer contains no useful data and should be treated as entirely uninitialized. 252 | * - Any segment whose data buffer overlaps with tempBuffer[0 : len] 253 | * must be treated as having invalid values in that array. 254 | * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). 255 | * 256 | * Please consult the QR Code specification for information on 257 | * data capacities per version, ECC level, and text encoding mode. 258 | * 259 | * This function allows the user to create a custom sequence of segments that switches 260 | * between modes (such as alphanumeric and byte) to encode text in less space. 261 | * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). 262 | */ 263 | bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, 264 | enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]); 265 | 266 | 267 | /* 268 | * Encodes the given segments to a QR Code, returning true if successful. 269 | * If the data is too long to fit in any version in the given range 270 | * at the given ECC level, then false is returned. 271 | * 272 | * Requires 1 <= minVersion <= maxVersion <= 40. 273 | * 274 | * The smallest possible QR Code version within the given range is automatically 275 | * chosen for the output. Iff boostEcl is true, then the ECC level of the result 276 | * may be higher than the ecl argument if it can be done without increasing the 277 | * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or 278 | * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow). 279 | * 280 | * About the byte arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX): 281 | * - Before calling the function: 282 | * - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow 283 | * reading and writing; hence each array must have a length of at least len. 284 | * - The two ranges must not overlap (aliasing). 285 | * - The initial state of both ranges can be uninitialized 286 | * because the function always writes before reading. 287 | * - The input array segs can contain segments whose data buffers overlap with tempBuffer. 288 | * - After the function returns: 289 | * - Both ranges have no guarantee on which elements are initialized and what values are stored. 290 | * - tempBuffer contains no useful data and should be treated as entirely uninitialized. 291 | * - Any segment whose data buffer overlaps with tempBuffer[0 : len] 292 | * must be treated as having invalid values in that array. 293 | * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). 294 | * 295 | * Please consult the QR Code specification for information on 296 | * data capacities per version, ECC level, and text encoding mode. 297 | * 298 | * This function allows the user to create a custom sequence of segments that switches 299 | * between modes (such as alphanumeric and byte) to encode text in less space. 300 | * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). 301 | */ 302 | bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, 303 | int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]); 304 | 305 | 306 | /* 307 | * Tests whether the given string can be encoded as a segment in numeric mode. 308 | * A string is encodable iff each character is in the range 0 to 9. 309 | */ 310 | bool qrcodegen_isNumeric(const char *text); 311 | 312 | 313 | /* 314 | * Tests whether the given string can be encoded as a segment in alphanumeric mode. 315 | * A string is encodable iff each character is in the following set: 0 to 9, A to Z 316 | * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. 317 | */ 318 | bool qrcodegen_isAlphanumeric(const char *text); 319 | 320 | 321 | /* 322 | * Returns the number of bytes (uint8_t) needed for the data buffer of a segment 323 | * containing the given number of characters using the given mode. Notes: 324 | * - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or the internal 325 | * calculation of the number of needed bits exceeds INT16_MAX (i.e. 32767). 326 | * - Otherwise, all valid results are in the range [0, ceil(INT16_MAX / 8)], i.e. at most 4096. 327 | * - It is okay for the user to allocate more bytes for the buffer than needed. 328 | * - For byte mode, numChars measures the number of bytes, not Unicode code points. 329 | * - For ECI mode, numChars must be 0, and the worst-case number of bytes is returned. 330 | * An actual ECI segment can have shorter data. For non-ECI modes, the result is exact. 331 | */ 332 | size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars); 333 | 334 | 335 | /* 336 | * Returns a segment representing the given binary data encoded in 337 | * byte mode. All input byte arrays are acceptable. Any text string 338 | * can be converted to UTF-8 bytes and encoded as a byte mode segment. 339 | */ 340 | struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]); 341 | 342 | 343 | /* 344 | * Returns a segment representing the given string of decimal digits encoded in numeric mode. 345 | */ 346 | struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]); 347 | 348 | 349 | /* 350 | * Returns a segment representing the given text string encoded in alphanumeric mode. 351 | * The characters allowed are: 0 to 9, A to Z (uppercase only), space, 352 | * dollar, percent, asterisk, plus, hyphen, period, slash, colon. 353 | */ 354 | struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]); 355 | 356 | 357 | /* 358 | * Returns a segment representing an Extended Channel Interpretation 359 | * (ECI) designator with the given assignment value. 360 | */ 361 | struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]); 362 | 363 | 364 | /*---- Functions to extract raw data from QR Codes ----*/ 365 | 366 | /* 367 | * Returns the side length of the given QR Code, assuming that encoding succeeded. 368 | * The result is in the range [21, 177]. Note that the length of the array buffer 369 | * is related to the side length - every 'uint8_t qrcode[]' must have length at least 370 | * qrcodegen_BUFFER_LEN_FOR_VERSION(version), which equals ceil(size^2 / 8 + 1). 371 | */ 372 | int qrcodegen_getSize(const uint8_t qrcode[]); 373 | 374 | 375 | /* 376 | * Returns the color of the module (pixel) at the given coordinates, which is false 377 | * for light or true for dark. The top left corner has the coordinates (x=0, y=0). 378 | * If the given coordinates are out of bounds, then false (light) is returned. 379 | */ 380 | bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y); 381 | 382 | 383 | #ifdef __cplusplus 384 | } 385 | #endif 386 | -------------------------------------------------------------------------------- /src/c/vendor/qrcodegen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * QR Code generator library (C) 3 | * 4 | * Copyright (c) Project Nayuki. (MIT License) 5 | * https://www.nayuki.io/page/qr-code-generator-library 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | * this software and associated documentation files (the "Software"), to deal in 9 | * the Software without restriction, including without limitation the rights to 10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | * the Software, and to permit persons to whom the Software is furnished to do so, 12 | * subject to the following conditions: 13 | * - The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * - The Software is provided "as is", without warranty of any kind, express or 16 | * implied, including but not limited to the warranties of merchantability, 17 | * fitness for a particular purpose and noninfringement. In no event shall the 18 | * authors or copyright holders be liable for any claim, damages or other 19 | * liability, whether in an action of contract, tort or otherwise, arising from, 20 | * out of or in connection with the Software or the use or other dealings in the 21 | * Software. 22 | */ 23 | 24 | #include 25 | #include "qrcodegen.h" 26 | 27 | #define assert(x) do {if (!(x)) { APP_LOG(APP_LOG_LEVEL_ERROR, "assertion failed: %s", #x);}} while(0) 28 | #define LONG_MAX 2147483647 29 | 30 | #ifndef QRCODEGEN_TEST 31 | #define testable static // Keep functions private 32 | #else 33 | #define testable // Expose private functions 34 | #endif 35 | 36 | char *strchr(const char *str, int ch) { 37 | for (;; str++) { 38 | if (*str == ch) return (char *)str; 39 | if (!*str) return NULL; 40 | } 41 | return NULL; 42 | } 43 | 44 | 45 | /*---- Forward declarations for private functions ----*/ 46 | 47 | // Regarding all public and private functions defined in this source file: 48 | // - They require all pointer/array arguments to be not null unless the array length is zero. 49 | // - They only read input scalar/array arguments, write to output pointer/array 50 | // arguments, and return scalar values; they are "pure" functions. 51 | // - They don't read mutable global variables or write to any global variables. 52 | // - They don't perform I/O, read the clock, print to console, etc. 53 | // - They allocate a small and constant amount of stack memory. 54 | // - They don't allocate or free any memory on the heap. 55 | // - They don't recurse or mutually recurse. All the code 56 | // could be inlined into the top-level public functions. 57 | // - They run in at most quadratic time with respect to input arguments. 58 | // Most functions run in linear time, and some in constant time. 59 | // There are no unbounded loops or non-obvious termination conditions. 60 | // - They are completely thread-safe if the caller does not give the 61 | // same writable buffer to concurrent calls to these functions. 62 | 63 | testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen); 64 | 65 | testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); 66 | testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); 67 | testable int getNumRawDataModules(int ver); 68 | 69 | testable void reedSolomonComputeDivisor(int degree, uint8_t result[]); 70 | testable void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, 71 | const uint8_t generator[], int degree, uint8_t result[]); 72 | testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y); 73 | 74 | testable void initializeFunctionModules(int version, uint8_t qrcode[]); 75 | static void drawLightFunctionModules(uint8_t qrcode[], int version); 76 | static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]); 77 | testable int getAlignmentPatternPositions(int version, uint8_t result[7]); 78 | static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]); 79 | 80 | static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]); 81 | static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask); 82 | static long getPenaltyScore(const uint8_t qrcode[]); 83 | static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize); 84 | static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize); 85 | static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize); 86 | 87 | testable bool getModuleBounded(const uint8_t qrcode[], int x, int y); 88 | testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark); 89 | testable void setModuleUnbounded(uint8_t qrcode[], int x, int y, bool isDark); 90 | static bool getBit(int x, int i); 91 | 92 | testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars); 93 | testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version); 94 | static int numCharCountBits(enum qrcodegen_Mode mode, int version); 95 | 96 | 97 | 98 | /*---- Private tables of constants ----*/ 99 | 100 | // The set of all legal characters in alphanumeric mode, where each character 101 | // value maps to the index in the string. For checking text and encoding segments. 102 | static const char *ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; 103 | 104 | // For generating error correction codes. 105 | testable const int8_t ECC_CODEWORDS_PER_BLOCK[4][3] = { 106 | // Version: (note that index 0 is for padding, and is set to an illegal value) 107 | //0, 1, 2 Error correction level 108 | {-1, 7, 10}, // Low 109 | {-1, 10, 16}, // Medium 110 | {-1, 13, 22}, // Quartile 111 | {-1, 17, 28}, // High 112 | }; 113 | 114 | #define qrcodegen_REED_SOLOMON_DEGREE_MAX 30 // Based on the table above 115 | 116 | // For generating error correction codes. 117 | testable const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][3] = { 118 | // Version: (note that index 0 is for padding, and is set to an illegal value) 119 | //0, 1, 2 Error correction level 120 | {-1, 1, 1}, // Low 121 | {-1, 1, 1}, // Medium 122 | {-1, 1, 1}, // Quartile 123 | {-1, 1, 1}, // High 124 | }; 125 | 126 | // For automatic mask pattern selection. 127 | static const int PENALTY_N1 = 3; 128 | static const int PENALTY_N2 = 3; 129 | static const int PENALTY_N3 = 40; 130 | static const int PENALTY_N4 = 10; 131 | 132 | 133 | 134 | /*---- High-level QR Code encoding functions ----*/ 135 | 136 | // Public function - see documentation comment in header file. 137 | bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], 138 | enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { 139 | 140 | size_t textLen = strlen(text); 141 | APP_LOG(APP_LOG_LEVEL_DEBUG, "text len: %d", textLen); 142 | if (textLen == 0) 143 | return qrcodegen_encodeSegmentsAdvanced(NULL, 0, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); 144 | size_t bufLen = (size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); 145 | APP_LOG(APP_LOG_LEVEL_DEBUG, "bufLen: %d", bufLen); 146 | 147 | struct qrcodegen_Segment seg; 148 | if (qrcodegen_isNumeric(text)) { 149 | if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, textLen) > bufLen) 150 | goto fail; 151 | seg = qrcodegen_makeNumeric(text, tempBuffer); 152 | } else if (qrcodegen_isAlphanumeric(text)) { 153 | if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ALPHANUMERIC, textLen) > bufLen) 154 | goto fail; 155 | seg = qrcodegen_makeAlphanumeric(text, tempBuffer); 156 | } else { 157 | if (textLen > bufLen) 158 | goto fail; 159 | for (size_t i = 0; i < textLen; i++) 160 | tempBuffer[i] = (uint8_t)text[i]; 161 | seg.mode = qrcodegen_Mode_BYTE; 162 | seg.bitLength = calcSegmentBitLength(seg.mode, textLen); 163 | if (seg.bitLength == -1) 164 | goto fail; 165 | seg.numChars = (int)textLen; 166 | seg.data = tempBuffer; 167 | } 168 | return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); 169 | 170 | fail: 171 | qrcode[0] = 0; // Set size to invalid value for safety 172 | return false; 173 | } 174 | 175 | 176 | // Public function - see documentation comment in header file. 177 | bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], 178 | enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { 179 | 180 | struct qrcodegen_Segment seg; 181 | seg.mode = qrcodegen_Mode_BYTE; 182 | seg.bitLength = calcSegmentBitLength(seg.mode, dataLen); 183 | if (seg.bitLength == -1) { 184 | qrcode[0] = 0; // Set size to invalid value for safety 185 | return false; 186 | } 187 | seg.numChars = (int)dataLen; 188 | seg.data = dataAndTemp; 189 | return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, dataAndTemp, qrcode); 190 | } 191 | 192 | 193 | // Appends the given number of low-order bits of the given value to the given byte-based 194 | // bit buffer, increasing the bit length. Requires 0 <= numBits <= 16 and val < 2^numBits. 195 | testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen) { 196 | assert(0 <= numBits && numBits <= 16 && (unsigned long)val >> numBits == 0); 197 | for (int i = numBits - 1; i >= 0; i--, (*bitLen)++) 198 | buffer[*bitLen >> 3] |= ((val >> i) & 1) << (7 - (*bitLen & 7)); 199 | } 200 | 201 | 202 | 203 | /*---- Low-level QR Code encoding functions ----*/ 204 | 205 | // Public function - see documentation comment in header file. 206 | bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, 207 | enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]) { 208 | return qrcodegen_encodeSegmentsAdvanced(segs, len, ecl, 209 | qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true, tempBuffer, qrcode); 210 | } 211 | 212 | 213 | // Public function - see documentation comment in header file. 214 | bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, 215 | int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]) { 216 | assert(segs != NULL || len == 0); 217 | assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX); 218 | assert((int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7); 219 | 220 | // Find the minimal version number to use 221 | int version, dataUsedBits; 222 | for (version = minVersion; ; version++) { 223 | int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available 224 | dataUsedBits = getTotalBits(segs, len, version); 225 | if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) 226 | break; // This version number is found to be suitable 227 | if (version >= maxVersion) { // All versions in the range could not fit the given data 228 | qrcode[0] = 0; // Set size to invalid value for safety 229 | return false; 230 | } 231 | } 232 | assert(dataUsedBits != -1); 233 | 234 | // Increase the error correction level while the data still fits in the current version number 235 | for (int i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) { // From low to high 236 | if (boostEcl && dataUsedBits <= getNumDataCodewords(version, (enum qrcodegen_Ecc)i) * 8) 237 | ecl = (enum qrcodegen_Ecc)i; 238 | } 239 | 240 | // Concatenate all segments to create the data bit string 241 | memset(qrcode, 0, (size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); 242 | int bitLen = 0; 243 | for (size_t i = 0; i < len; i++) { 244 | const struct qrcodegen_Segment *seg = &segs[i]; 245 | appendBitsToBuffer((unsigned int)seg->mode, 4, qrcode, &bitLen); 246 | appendBitsToBuffer((unsigned int)seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); 247 | for (int j = 0; j < seg->bitLength; j++) { 248 | int bit = (seg->data[j >> 3] >> (7 - (j & 7))) & 1; 249 | appendBitsToBuffer((unsigned int)bit, 1, qrcode, &bitLen); 250 | } 251 | } 252 | assert(bitLen == dataUsedBits); 253 | 254 | // Add terminator and pad up to a byte if applicable 255 | int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; 256 | assert(bitLen <= dataCapacityBits); 257 | int terminatorBits = dataCapacityBits - bitLen; 258 | if (terminatorBits > 4) 259 | terminatorBits = 4; 260 | appendBitsToBuffer(0, terminatorBits, qrcode, &bitLen); 261 | appendBitsToBuffer(0, (8 - bitLen % 8) % 8, qrcode, &bitLen); 262 | assert(bitLen % 8 == 0); 263 | 264 | // Pad with alternating bytes until data capacity is reached 265 | for (uint8_t padByte = 0xEC; bitLen < dataCapacityBits; padByte ^= 0xEC ^ 0x11) 266 | appendBitsToBuffer(padByte, 8, qrcode, &bitLen); 267 | 268 | // Compute ECC, draw modules 269 | addEccAndInterleave(qrcode, version, ecl, tempBuffer); 270 | initializeFunctionModules(version, qrcode); 271 | drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode); 272 | drawLightFunctionModules(qrcode, version); 273 | initializeFunctionModules(version, tempBuffer); 274 | 275 | // Do masking 276 | if (mask == qrcodegen_Mask_AUTO) { // Automatically choose best mask 277 | #ifndef PBL_PLATFORM_APLITE 278 | long minPenalty = LONG_MAX; 279 | for (int i = 0; i < 8; i++) { 280 | enum qrcodegen_Mask msk = (enum qrcodegen_Mask)i; 281 | applyMask(tempBuffer, qrcode, msk); 282 | drawFormatBits(ecl, msk, qrcode); 283 | long penalty = getPenaltyScore(qrcode); 284 | if (penalty < minPenalty) { 285 | mask = msk; 286 | minPenalty = penalty; 287 | } 288 | applyMask(tempBuffer, qrcode, msk); // Undoes the mask due to XOR 289 | } 290 | #else 291 | mask = qrcodegen_Mask_0; 292 | #endif 293 | } 294 | assert(0 <= (int)mask && (int)mask <= 7); 295 | applyMask(tempBuffer, qrcode, mask); // Apply the final choice of mask 296 | drawFormatBits(ecl, mask, qrcode); // Overwrite old format bits 297 | return true; 298 | } 299 | 300 | 301 | 302 | /*---- Error correction code generation functions ----*/ 303 | 304 | // Appends error correction bytes to each block of the given data array, then interleaves 305 | // bytes from the blocks and stores them in the result array. data[0 : dataLen] contains 306 | // the input data. data[dataLen : rawCodewords] is used as a temporary work area and will 307 | // be clobbered by this function. The final answer is stored in result[0 : rawCodewords]. 308 | testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]) { 309 | // Calculate parameter numbers 310 | assert((int)ecl < 4 && qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); 311 | int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; 312 | int blockEccLen = ECC_CODEWORDS_PER_BLOCK [(int)ecl][version]; 313 | int rawCodewords = getNumRawDataModules(version) / 8; 314 | int dataLen = getNumDataCodewords(version, ecl); 315 | int numShortBlocks = numBlocks - rawCodewords % numBlocks; 316 | int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen; 317 | 318 | // Split data into blocks, calculate ECC, and interleave 319 | // (not concatenate) the bytes into a single sequence 320 | uint8_t rsdiv[qrcodegen_REED_SOLOMON_DEGREE_MAX]; 321 | reedSolomonComputeDivisor(blockEccLen, rsdiv); 322 | const uint8_t *dat = data; 323 | for (int i = 0; i < numBlocks; i++) { 324 | int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); 325 | uint8_t *ecc = &data[dataLen]; // Temporary storage 326 | reedSolomonComputeRemainder(dat, datLen, rsdiv, blockEccLen, ecc); 327 | for (int j = 0, k = i; j < datLen; j++, k += numBlocks) { // Copy data 328 | if (j == shortBlockDataLen) 329 | k -= numShortBlocks; 330 | result[k] = dat[j]; 331 | } 332 | for (int j = 0, k = dataLen + i; j < blockEccLen; j++, k += numBlocks) // Copy ECC 333 | result[k] = ecc[j]; 334 | dat += datLen; 335 | } 336 | } 337 | 338 | 339 | // Returns the number of 8-bit codewords that can be used for storing data (not ECC), 340 | // for the given version number and error correction level. The result is in the range [9, 2956]. 341 | testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl) { 342 | int v = version, e = (int)ecl; 343 | assert(0 <= e && e < 4); 344 | return getNumRawDataModules(v) / 8 345 | - ECC_CODEWORDS_PER_BLOCK [e][v] 346 | * NUM_ERROR_CORRECTION_BLOCKS[e][v]; 347 | } 348 | 349 | 350 | // Returns the number of data bits that can be stored in a QR Code of the given version number, after 351 | // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. 352 | // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. 353 | testable int getNumRawDataModules(int ver) { 354 | assert(qrcodegen_VERSION_MIN <= ver && ver <= qrcodegen_VERSION_MAX); 355 | int result = (16 * ver + 128) * ver + 64; 356 | if (ver >= 2) { 357 | int numAlign = ver / 7 + 2; 358 | result -= (25 * numAlign - 10) * numAlign - 55; 359 | if (ver >= 7) 360 | result -= 36; 361 | } 362 | assert(208 <= result && result <= 29648); 363 | return result; 364 | } 365 | 366 | 367 | 368 | /*---- Reed-Solomon ECC generator functions ----*/ 369 | 370 | // Computes a Reed-Solomon ECC generator polynomial for the given degree, storing in result[0 : degree]. 371 | // This could be implemented as a lookup table over all possible parameter values, instead of as an algorithm. 372 | testable void reedSolomonComputeDivisor(int degree, uint8_t result[]) { 373 | assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); 374 | // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. 375 | // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. 376 | memset(result, 0, (size_t)degree * sizeof(result[0])); 377 | result[degree - 1] = 1; // Start off with the monomial x^0 378 | 379 | // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), 380 | // drop the highest monomial term which is always 1x^degree. 381 | // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). 382 | uint8_t root = 1; 383 | for (int i = 0; i < degree; i++) { 384 | // Multiply the current product by (x - r^i) 385 | for (int j = 0; j < degree; j++) { 386 | result[j] = reedSolomonMultiply(result[j], root); 387 | if (j + 1 < degree) 388 | result[j] ^= result[j + 1]; 389 | } 390 | root = reedSolomonMultiply(root, 0x02); 391 | } 392 | } 393 | 394 | 395 | // Computes the Reed-Solomon error correction codeword for the given data and divisor polynomials. 396 | // The remainder when data[0 : dataLen] is divided by divisor[0 : degree] is stored in result[0 : degree]. 397 | // All polynomials are in big endian, and the generator has an implicit leading 1 term. 398 | testable void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, 399 | const uint8_t generator[], int degree, uint8_t result[]) { 400 | assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); 401 | memset(result, 0, (size_t)degree * sizeof(result[0])); 402 | for (int i = 0; i < dataLen; i++) { // Polynomial division 403 | uint8_t factor = data[i] ^ result[0]; 404 | memmove(&result[0], &result[1], (size_t)(degree - 1) * sizeof(result[0])); 405 | result[degree - 1] = 0; 406 | for (int j = 0; j < degree; j++) 407 | result[j] ^= reedSolomonMultiply(generator[j], factor); 408 | } 409 | } 410 | 411 | #undef qrcodegen_REED_SOLOMON_DEGREE_MAX 412 | 413 | 414 | // Returns the product of the two given field elements modulo GF(2^8/0x11D). 415 | // All inputs are valid. This could be implemented as a 256*256 lookup table. 416 | testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y) { 417 | // Russian peasant multiplication 418 | uint8_t z = 0; 419 | for (int i = 7; i >= 0; i--) { 420 | z = (uint8_t)((z << 1) ^ ((z >> 7) * 0x11D)); 421 | z ^= ((y >> i) & 1) * x; 422 | } 423 | return z; 424 | } 425 | 426 | 427 | 428 | /*---- Drawing function modules ----*/ 429 | 430 | // Clears the given QR Code grid with light modules for the given 431 | // version's size, then marks every function module as dark. 432 | testable void initializeFunctionModules(int version, uint8_t qrcode[]) { 433 | // Initialize QR Code 434 | int qrsize = version * 4 + 17; 435 | memset(qrcode, 0, (size_t)((qrsize * qrsize + 7) / 8 + 1) * sizeof(qrcode[0])); 436 | qrcode[0] = (uint8_t)qrsize; 437 | 438 | // Fill horizontal and vertical timing patterns 439 | fillRectangle(6, 0, 1, qrsize, qrcode); 440 | fillRectangle(0, 6, qrsize, 1, qrcode); 441 | 442 | // Fill 3 finder patterns (all corners except bottom right) and format bits 443 | fillRectangle(0, 0, 9, 9, qrcode); 444 | fillRectangle(qrsize - 8, 0, 8, 9, qrcode); 445 | fillRectangle(0, qrsize - 8, 9, 8, qrcode); 446 | 447 | // Fill numerous alignment patterns 448 | uint8_t alignPatPos[7]; 449 | int numAlign = getAlignmentPatternPositions(version, alignPatPos); 450 | for (int i = 0; i < numAlign; i++) { 451 | for (int j = 0; j < numAlign; j++) { 452 | // Don't draw on the three finder corners 453 | if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) 454 | fillRectangle(alignPatPos[i] - 2, alignPatPos[j] - 2, 5, 5, qrcode); 455 | } 456 | } 457 | 458 | // Fill version blocks 459 | if (version >= 7) { 460 | fillRectangle(qrsize - 11, 0, 3, 6, qrcode); 461 | fillRectangle(0, qrsize - 11, 6, 3, qrcode); 462 | } 463 | } 464 | 465 | 466 | // Draws light function modules and possibly some dark modules onto the given QR Code, without changing 467 | // non-function modules. This does not draw the format bits. This requires all function modules to be previously 468 | // marked dark (namely by initializeFunctionModules()), because this may skip redrawing dark function modules. 469 | static void drawLightFunctionModules(uint8_t qrcode[], int version) { 470 | // Draw horizontal and vertical timing patterns 471 | int qrsize = qrcodegen_getSize(qrcode); 472 | for (int i = 7; i < qrsize - 7; i += 2) { 473 | setModuleBounded(qrcode, 6, i, false); 474 | setModuleBounded(qrcode, i, 6, false); 475 | } 476 | 477 | // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) 478 | for (int dy = -4; dy <= 4; dy++) { 479 | for (int dx = -4; dx <= 4; dx++) { 480 | int dist = abs(dx); 481 | if (abs(dy) > dist) 482 | dist = abs(dy); 483 | if (dist == 2 || dist == 4) { 484 | setModuleUnbounded(qrcode, 3 + dx, 3 + dy, false); 485 | setModuleUnbounded(qrcode, qrsize - 4 + dx, 3 + dy, false); 486 | setModuleUnbounded(qrcode, 3 + dx, qrsize - 4 + dy, false); 487 | } 488 | } 489 | } 490 | 491 | // Draw numerous alignment patterns 492 | uint8_t alignPatPos[7]; 493 | int numAlign = getAlignmentPatternPositions(version, alignPatPos); 494 | for (int i = 0; i < numAlign; i++) { 495 | for (int j = 0; j < numAlign; j++) { 496 | if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) 497 | continue; // Don't draw on the three finder corners 498 | for (int dy = -1; dy <= 1; dy++) { 499 | for (int dx = -1; dx <= 1; dx++) 500 | setModuleBounded(qrcode, alignPatPos[i] + dx, alignPatPos[j] + dy, dx == 0 && dy == 0); 501 | } 502 | } 503 | } 504 | 505 | // Draw version blocks 506 | if (version >= 7) { 507 | // Calculate error correction code and pack bits 508 | int rem = version; // version is uint6, in the range [7, 40] 509 | for (int i = 0; i < 12; i++) 510 | rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); 511 | long bits = (long)version << 12 | rem; // uint18 512 | assert(bits >> 18 == 0); 513 | 514 | // Draw two copies 515 | for (int i = 0; i < 6; i++) { 516 | for (int j = 0; j < 3; j++) { 517 | int k = qrsize - 11 + j; 518 | setModuleBounded(qrcode, k, i, (bits & 1) != 0); 519 | setModuleBounded(qrcode, i, k, (bits & 1) != 0); 520 | bits >>= 1; 521 | } 522 | } 523 | } 524 | } 525 | 526 | 527 | // Draws two copies of the format bits (with its own error correction code) based 528 | // on the given mask and error correction level. This always draws all modules of 529 | // the format bits, unlike drawLightFunctionModules() which might skip dark modules. 530 | static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]) { 531 | // Calculate error correction code and pack bits 532 | assert(0 <= (int)mask && (int)mask <= 7); 533 | static const int table[] = {1, 0, 3, 2}; 534 | int data = table[(int)ecl] << 3 | (int)mask; // errCorrLvl is uint2, mask is uint3 535 | int rem = data; 536 | for (int i = 0; i < 10; i++) 537 | rem = (rem << 1) ^ ((rem >> 9) * 0x537); 538 | int bits = (data << 10 | rem) ^ 0x5412; // uint15 539 | assert(bits >> 15 == 0); 540 | 541 | // Draw first copy 542 | for (int i = 0; i <= 5; i++) 543 | setModuleBounded(qrcode, 8, i, getBit(bits, i)); 544 | setModuleBounded(qrcode, 8, 7, getBit(bits, 6)); 545 | setModuleBounded(qrcode, 8, 8, getBit(bits, 7)); 546 | setModuleBounded(qrcode, 7, 8, getBit(bits, 8)); 547 | for (int i = 9; i < 15; i++) 548 | setModuleBounded(qrcode, 14 - i, 8, getBit(bits, i)); 549 | 550 | // Draw second copy 551 | int qrsize = qrcodegen_getSize(qrcode); 552 | for (int i = 0; i < 8; i++) 553 | setModuleBounded(qrcode, qrsize - 1 - i, 8, getBit(bits, i)); 554 | for (int i = 8; i < 15; i++) 555 | setModuleBounded(qrcode, 8, qrsize - 15 + i, getBit(bits, i)); 556 | setModuleBounded(qrcode, 8, qrsize - 8, true); // Always dark 557 | } 558 | 559 | 560 | // Calculates and stores an ascending list of positions of alignment patterns 561 | // for this version number, returning the length of the list (in the range [0,7]). 562 | // Each position is in the range [0,177), and are used on both the x and y axes. 563 | // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. 564 | testable int getAlignmentPatternPositions(int version, uint8_t result[7]) { 565 | if (version == 1) 566 | return 0; 567 | int numAlign = version / 7 + 2; 568 | int step = (version == 32) ? 26 : 569 | (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2; 570 | for (int i = numAlign - 1, pos = version * 4 + 10; i >= 1; i--, pos -= step) 571 | result[i] = (uint8_t)pos; 572 | result[0] = 6; 573 | return numAlign; 574 | } 575 | 576 | 577 | // Sets every module in the range [left : left + width] * [top : top + height] to dark. 578 | static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]) { 579 | for (int dy = 0; dy < height; dy++) { 580 | for (int dx = 0; dx < width; dx++) 581 | setModuleBounded(qrcode, left + dx, top + dy, true); 582 | } 583 | } 584 | 585 | 586 | 587 | /*---- Drawing data modules and masking ----*/ 588 | 589 | // Draws the raw codewords (including data and ECC) onto the given QR Code. This requires the initial state of 590 | // the QR Code to be dark at function modules and light at codeword modules (including unused remainder bits). 591 | static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) { 592 | int qrsize = qrcodegen_getSize(qrcode); 593 | int i = 0; // Bit index into the data 594 | // Do the funny zigzag scan 595 | for (int right = qrsize - 1; right >= 1; right -= 2) { // Index of right column in each column pair 596 | if (right == 6) 597 | right = 5; 598 | for (int vert = 0; vert < qrsize; vert++) { // Vertical counter 599 | for (int j = 0; j < 2; j++) { 600 | int x = right - j; // Actual x coordinate 601 | bool upward = ((right + 1) & 2) == 0; 602 | int y = upward ? qrsize - 1 - vert : vert; // Actual y coordinate 603 | if (!getModuleBounded(qrcode, x, y) && i < dataLen * 8) { 604 | bool dark = getBit(data[i >> 3], 7 - (i & 7)); 605 | setModuleBounded(qrcode, x, y, dark); 606 | i++; 607 | } 608 | // If this QR Code has any remainder bits (0 to 7), they were assigned as 609 | // 0/false/light by the constructor and are left unchanged by this method 610 | } 611 | } 612 | } 613 | assert(i == dataLen * 8); 614 | } 615 | 616 | 617 | // XORs the codeword modules in this QR Code with the given mask pattern 618 | // and given pattern of function modules. The codeword bits must be drawn 619 | // before masking. Due to the arithmetic of XOR, calling applyMask() with 620 | // the same mask value a second time will undo the mask. A final well-formed 621 | // QR Code needs exactly one (not zero, two, etc.) mask applied. 622 | static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask) { 623 | assert(0 <= (int)mask && (int)mask <= 7); // Disallows qrcodegen_Mask_AUTO 624 | int qrsize = qrcodegen_getSize(qrcode); 625 | for (int y = 0; y < qrsize; y++) { 626 | for (int x = 0; x < qrsize; x++) { 627 | if (getModuleBounded(functionModules, x, y)) 628 | continue; 629 | bool invert; 630 | switch ((int)mask) { 631 | case 0: invert = (x + y) % 2 == 0; break; 632 | case 1: invert = y % 2 == 0; break; 633 | case 2: invert = x % 3 == 0; break; 634 | case 3: invert = (x + y) % 3 == 0; break; 635 | case 4: invert = (x / 3 + y / 2) % 2 == 0; break; 636 | case 5: invert = x * y % 2 + x * y % 3 == 0; break; 637 | case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; 638 | case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; 639 | default: assert(false); return; 640 | } 641 | bool val = getModuleBounded(qrcode, x, y); 642 | setModuleBounded(qrcode, x, y, val ^ invert); 643 | } 644 | } 645 | } 646 | 647 | 648 | // Calculates and returns the penalty score based on state of the given QR Code's current modules. 649 | // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. 650 | static long getPenaltyScore(const uint8_t qrcode[]) { 651 | int qrsize = qrcodegen_getSize(qrcode); 652 | long result = 0; 653 | 654 | // Adjacent modules in row having same color, and finder-like patterns 655 | for (int y = 0; y < qrsize; y++) { 656 | bool runColor = false; 657 | int runX = 0; 658 | int runHistory[7] = {0}; 659 | for (int x = 0; x < qrsize; x++) { 660 | if (getModuleBounded(qrcode, x, y) == runColor) { 661 | runX++; 662 | if (runX == 5) 663 | result += PENALTY_N1; 664 | else if (runX > 5) 665 | result++; 666 | } else { 667 | finderPenaltyAddHistory(runX, runHistory, qrsize); 668 | if (!runColor) 669 | result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3; 670 | runColor = getModuleBounded(qrcode, x, y); 671 | runX = 1; 672 | } 673 | } 674 | result += finderPenaltyTerminateAndCount(runColor, runX, runHistory, qrsize) * PENALTY_N3; 675 | } 676 | // Adjacent modules in column having same color, and finder-like patterns 677 | for (int x = 0; x < qrsize; x++) { 678 | bool runColor = false; 679 | int runY = 0; 680 | int runHistory[7] = {0}; 681 | for (int y = 0; y < qrsize; y++) { 682 | if (getModuleBounded(qrcode, x, y) == runColor) { 683 | runY++; 684 | if (runY == 5) 685 | result += PENALTY_N1; 686 | else if (runY > 5) 687 | result++; 688 | } else { 689 | finderPenaltyAddHistory(runY, runHistory, qrsize); 690 | if (!runColor) 691 | result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3; 692 | runColor = getModuleBounded(qrcode, x, y); 693 | runY = 1; 694 | } 695 | } 696 | result += finderPenaltyTerminateAndCount(runColor, runY, runHistory, qrsize) * PENALTY_N3; 697 | } 698 | 699 | // 2*2 blocks of modules having same color 700 | for (int y = 0; y < qrsize - 1; y++) { 701 | for (int x = 0; x < qrsize - 1; x++) { 702 | bool color = getModuleBounded(qrcode, x, y); 703 | if ( color == getModuleBounded(qrcode, x + 1, y) && 704 | color == getModuleBounded(qrcode, x, y + 1) && 705 | color == getModuleBounded(qrcode, x + 1, y + 1)) 706 | result += PENALTY_N2; 707 | } 708 | } 709 | 710 | // Balance of dark and light modules 711 | int dark = 0; 712 | for (int y = 0; y < qrsize; y++) { 713 | for (int x = 0; x < qrsize; x++) { 714 | if (getModuleBounded(qrcode, x, y)) 715 | dark++; 716 | } 717 | } 718 | int total = qrsize * qrsize; // Note that size is odd, so dark/total != 1/2 719 | // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% 720 | int k = (int)((labs(dark * 20L - total * 10L) + total - 1) / total) - 1; 721 | assert(0 <= k && k <= 9); 722 | result += k * PENALTY_N4; 723 | assert(0 <= result && result <= 2568888L); // Non-tight upper bound based on default values of PENALTY_N1, ..., N4 724 | return result; 725 | } 726 | 727 | 728 | // Can only be called immediately after a light run is added, and 729 | // returns either 0, 1, or 2. A helper function for getPenaltyScore(). 730 | static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize) { 731 | int n = runHistory[1]; 732 | assert(n <= qrsize * 3); (void)qrsize; 733 | bool core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n; 734 | // The maximum QR Code size is 177, hence the dark run length n <= 177. 735 | // Arithmetic is promoted to int, so n*4 will not overflow. 736 | return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0) 737 | + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0); 738 | } 739 | 740 | 741 | // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). 742 | static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize) { 743 | if (currentRunColor) { // Terminate dark run 744 | finderPenaltyAddHistory(currentRunLength, runHistory, qrsize); 745 | currentRunLength = 0; 746 | } 747 | currentRunLength += qrsize; // Add light border to final run 748 | finderPenaltyAddHistory(currentRunLength, runHistory, qrsize); 749 | return finderPenaltyCountPatterns(runHistory, qrsize); 750 | } 751 | 752 | 753 | // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). 754 | static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize) { 755 | if (runHistory[0] == 0) 756 | currentRunLength += qrsize; // Add light border to initial run 757 | memmove(&runHistory[1], &runHistory[0], 6 * sizeof(runHistory[0])); 758 | runHistory[0] = currentRunLength; 759 | } 760 | 761 | 762 | 763 | /*---- Basic QR Code information ----*/ 764 | 765 | // Public function - see documentation comment in header file. 766 | int qrcodegen_getSize(const uint8_t qrcode[]) { 767 | assert(qrcode != NULL); 768 | int result = qrcode[0]; 769 | assert((qrcodegen_VERSION_MIN * 4 + 17) <= result 770 | && result <= (qrcodegen_VERSION_MAX * 4 + 17)); 771 | return result; 772 | } 773 | 774 | 775 | // Public function - see documentation comment in header file. 776 | bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y) { 777 | assert(qrcode != NULL); 778 | int qrsize = qrcode[0]; 779 | return (0 <= x && x < qrsize && 0 <= y && y < qrsize) && getModuleBounded(qrcode, x, y); 780 | } 781 | 782 | 783 | // Returns the color of the module at the given coordinates, which must be in bounds. 784 | testable bool getModuleBounded(const uint8_t qrcode[], int x, int y) { 785 | int qrsize = qrcode[0]; 786 | assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); 787 | int index = y * qrsize + x; 788 | return getBit(qrcode[(index >> 3) + 1], index & 7); 789 | } 790 | 791 | 792 | // Sets the color of the module at the given coordinates, which must be in bounds. 793 | testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark) { 794 | int qrsize = qrcode[0]; 795 | assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); 796 | int index = y * qrsize + x; 797 | int bitIndex = index & 7; 798 | int byteIndex = (index >> 3) + 1; 799 | if (isDark) 800 | qrcode[byteIndex] |= 1 << bitIndex; 801 | else 802 | qrcode[byteIndex] &= (1 << bitIndex) ^ 0xFF; 803 | } 804 | 805 | 806 | // Sets the color of the module at the given coordinates, doing nothing if out of bounds. 807 | testable void setModuleUnbounded(uint8_t qrcode[], int x, int y, bool isDark) { 808 | int qrsize = qrcode[0]; 809 | if (0 <= x && x < qrsize && 0 <= y && y < qrsize) 810 | setModuleBounded(qrcode, x, y, isDark); 811 | } 812 | 813 | 814 | // Returns true iff the i'th bit of x is set to 1. Requires x >= 0 and 0 <= i <= 14. 815 | static bool getBit(int x, int i) { 816 | return ((x >> i) & 1) != 0; 817 | } 818 | 819 | 820 | 821 | /*---- Segment handling ----*/ 822 | 823 | // Public function - see documentation comment in header file. 824 | bool qrcodegen_isNumeric(const char *text) { 825 | assert(text != NULL); 826 | for (; *text != '\0'; text++) { 827 | if (*text < '0' || *text > '9') 828 | return false; 829 | } 830 | return true; 831 | } 832 | 833 | 834 | // Public function - see documentation comment in header file. 835 | bool qrcodegen_isAlphanumeric(const char *text) { 836 | assert(text != NULL); 837 | for (; *text != '\0'; text++) { 838 | APP_LOG(APP_LOG_LEVEL_DEBUG, "inside is_alphanumeric"); 839 | if (strchr(ALPHANUMERIC_CHARSET, *text) == NULL) 840 | return false; 841 | } 842 | APP_LOG(APP_LOG_LEVEL_DEBUG, "done"); 843 | return true; 844 | } 845 | 846 | 847 | // Public function - see documentation comment in header file. 848 | size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars) { 849 | int temp = calcSegmentBitLength(mode, numChars); 850 | if (temp == -1) 851 | return SIZE_MAX; 852 | assert(0 <= temp && temp <= INT16_MAX); 853 | return ((size_t)temp + 7) / 8; 854 | } 855 | 856 | 857 | // Returns the number of data bits needed to represent a segment 858 | // containing the given number of characters using the given mode. Notes: 859 | // - Returns -1 on failure, i.e. numChars > INT16_MAX or 860 | // the number of needed bits exceeds INT16_MAX (i.e. 32767). 861 | // - Otherwise, all valid results are in the range [0, INT16_MAX]. 862 | // - For byte mode, numChars measures the number of bytes, not Unicode code points. 863 | // - For ECI mode, numChars must be 0, and the worst-case number of bits is returned. 864 | // An actual ECI segment can have shorter data. For non-ECI modes, the result is exact. 865 | testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars) { 866 | // All calculations are designed to avoid overflow on all platforms 867 | if (numChars > (unsigned int)INT16_MAX) 868 | return -1; 869 | long result = (long)numChars; 870 | if (mode == qrcodegen_Mode_NUMERIC) 871 | result = (result * 10 + 2) / 3; // ceil(10/3 * n) 872 | else if (mode == qrcodegen_Mode_ALPHANUMERIC) 873 | result = (result * 11 + 1) / 2; // ceil(11/2 * n) 874 | else if (mode == qrcodegen_Mode_BYTE) 875 | result *= 8; 876 | else if (mode == qrcodegen_Mode_KANJI) 877 | result *= 13; 878 | else if (mode == qrcodegen_Mode_ECI && numChars == 0) 879 | result = 3 * 8; 880 | else { // Invalid argument 881 | assert(false); 882 | return -1; 883 | } 884 | assert(result >= 0); 885 | if (result > INT16_MAX) 886 | return -1; 887 | return (int)result; 888 | } 889 | 890 | 891 | // Public function - see documentation comment in header file. 892 | struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]) { 893 | assert(data != NULL || len == 0); 894 | struct qrcodegen_Segment result; 895 | result.mode = qrcodegen_Mode_BYTE; 896 | result.bitLength = calcSegmentBitLength(result.mode, len); 897 | assert(result.bitLength != -1); 898 | result.numChars = (int)len; 899 | if (len > 0) 900 | memcpy(buf, data, len * sizeof(buf[0])); 901 | result.data = buf; 902 | return result; 903 | } 904 | 905 | 906 | // Public function - see documentation comment in header file. 907 | struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]) { 908 | assert(digits != NULL); 909 | struct qrcodegen_Segment result; 910 | size_t len = strlen(digits); 911 | result.mode = qrcodegen_Mode_NUMERIC; 912 | int bitLen = calcSegmentBitLength(result.mode, len); 913 | assert(bitLen != -1); 914 | result.numChars = (int)len; 915 | if (bitLen > 0) 916 | memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); 917 | result.bitLength = 0; 918 | 919 | unsigned int accumData = 0; 920 | int accumCount = 0; 921 | for (; *digits != '\0'; digits++) { 922 | char c = *digits; 923 | assert('0' <= c && c <= '9'); 924 | accumData = accumData * 10 + (unsigned int)(c - '0'); 925 | accumCount++; 926 | if (accumCount == 3) { 927 | appendBitsToBuffer(accumData, 10, buf, &result.bitLength); 928 | accumData = 0; 929 | accumCount = 0; 930 | } 931 | } 932 | if (accumCount > 0) // 1 or 2 digits remaining 933 | appendBitsToBuffer(accumData, accumCount * 3 + 1, buf, &result.bitLength); 934 | assert(result.bitLength == bitLen); 935 | result.data = buf; 936 | return result; 937 | } 938 | 939 | 940 | // Public function - see documentation comment in header file. 941 | struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]) { 942 | assert(text != NULL); 943 | struct qrcodegen_Segment result; 944 | size_t len = strlen(text); 945 | result.mode = qrcodegen_Mode_ALPHANUMERIC; 946 | int bitLen = calcSegmentBitLength(result.mode, len); 947 | assert(bitLen != -1); 948 | result.numChars = (int)len; 949 | if (bitLen > 0) 950 | memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); 951 | result.bitLength = 0; 952 | 953 | unsigned int accumData = 0; 954 | int accumCount = 0; 955 | for (; *text != '\0'; text++) { 956 | const char *temp = strchr(ALPHANUMERIC_CHARSET, *text); 957 | assert(temp != NULL); 958 | accumData = accumData * 45 + (unsigned int)(temp - ALPHANUMERIC_CHARSET); 959 | accumCount++; 960 | if (accumCount == 2) { 961 | appendBitsToBuffer(accumData, 11, buf, &result.bitLength); 962 | accumData = 0; 963 | accumCount = 0; 964 | } 965 | } 966 | if (accumCount > 0) // 1 character remaining 967 | appendBitsToBuffer(accumData, 6, buf, &result.bitLength); 968 | assert(result.bitLength == bitLen); 969 | result.data = buf; 970 | return result; 971 | } 972 | 973 | 974 | // Public function - see documentation comment in header file. 975 | struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]) { 976 | struct qrcodegen_Segment result; 977 | result.mode = qrcodegen_Mode_ECI; 978 | result.numChars = 0; 979 | result.bitLength = 0; 980 | if (assignVal < 0) 981 | assert(false); 982 | else if (assignVal < (1 << 7)) { 983 | memset(buf, 0, 1 * sizeof(buf[0])); 984 | appendBitsToBuffer((unsigned int)assignVal, 8, buf, &result.bitLength); 985 | } else if (assignVal < (1 << 14)) { 986 | memset(buf, 0, 2 * sizeof(buf[0])); 987 | appendBitsToBuffer(2, 2, buf, &result.bitLength); 988 | appendBitsToBuffer((unsigned int)assignVal, 14, buf, &result.bitLength); 989 | } else if (assignVal < 1000000L) { 990 | memset(buf, 0, 3 * sizeof(buf[0])); 991 | appendBitsToBuffer(6, 3, buf, &result.bitLength); 992 | appendBitsToBuffer((unsigned int)(assignVal >> 10), 11, buf, &result.bitLength); 993 | appendBitsToBuffer((unsigned int)(assignVal & 0x3FF), 10, buf, &result.bitLength); 994 | } else 995 | assert(false); 996 | result.data = buf; 997 | return result; 998 | } 999 | 1000 | 1001 | // Calculates the number of bits needed to encode the given segments at the given version. 1002 | // Returns a non-negative number if successful. Otherwise returns -1 if a segment has too 1003 | // many characters to fit its length field, or the total bits exceeds INT16_MAX. 1004 | testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version) { 1005 | assert(segs != NULL || len == 0); 1006 | long result = 0; 1007 | for (size_t i = 0; i < len; i++) { 1008 | int numChars = segs[i].numChars; 1009 | int bitLength = segs[i].bitLength; 1010 | assert(0 <= numChars && numChars <= INT16_MAX); 1011 | assert(0 <= bitLength && bitLength <= INT16_MAX); 1012 | int ccbits = numCharCountBits(segs[i].mode, version); 1013 | assert(0 <= ccbits && ccbits <= 16); 1014 | if (numChars >= (1L << ccbits)) 1015 | return -1; // The segment's length doesn't fit the field's bit width 1016 | result += 4L + ccbits + bitLength; 1017 | if (result > INT16_MAX) 1018 | return -1; // The sum might overflow an int type 1019 | } 1020 | assert(0 <= result && result <= INT16_MAX); 1021 | return (int)result; 1022 | } 1023 | 1024 | 1025 | // Returns the bit width of the character count field for a segment in the given mode 1026 | // in a QR Code at the given version number. The result is in the range [0, 16]. 1027 | static int numCharCountBits(enum qrcodegen_Mode mode, int version) { 1028 | assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); 1029 | int i = (version + 7) / 17; 1030 | switch (mode) { 1031 | case qrcodegen_Mode_NUMERIC : { static const int temp[] = {10, 12, 14}; return temp[i]; } 1032 | case qrcodegen_Mode_ALPHANUMERIC: { static const int temp[] = { 9, 11, 13}; return temp[i]; } 1033 | case qrcodegen_Mode_BYTE : { static const int temp[] = { 8, 16, 16}; return temp[i]; } 1034 | case qrcodegen_Mode_KANJI : { static const int temp[] = { 8, 10, 12}; return temp[i]; } 1035 | case qrcodegen_Mode_ECI : return 0; 1036 | default: assert(false); return -1; // Dummy value 1037 | } 1038 | } 1039 | -------------------------------------------------------------------------------- /resources/acceptable.dat: -------------------------------------------------------------------------------- 1 | aahedaaliiaarghaartiabacaabaciabackabacsabaftabakaabampabandabaseabashabaskabateabayaabbasabbedabbesabbeyabbotabceeabeamabearabeleabersabetsabhorabideabiesabledablerablesabletablowabmhoabodeabohmaboilabomaaboonabordaboreabortaboutaboveabramabrayabrimabrinabrisabseyabsitabunaabuneabuseabutsabuzzabyesabysmabyssacaisacariaccasaccoyacerbacersacetaacharachedachesachooacidsacidyacingaciniackeeackeracmesacmicacnedacnesacockacoldacornacredacresacridacrosactedactinactonactoracuteacylsadageadaptadawsadaysadbotaddaxaddedadderaddioaddleadeemadeptadhanadieuadiosaditsadmanadmenadminadmitadmixadobeadoboadoptadoreadornadownadozeadradadredadsumadukiadultaduncadustadvewadytaadzedadzesaeciaaedesaegisaeonsaerieaerosaesirafaldafaraafarsafearaffixafireaflajafootaforeafoulafritafrosafteragainagamaagamiagapeagarsagastagateagaveagazeageneagentagersaggeraggieaggriaggroaggryaghasagilaagileagingagiosagismagistagitaagleeagletagleyaglooaglowaglusagmasagogeagoneagonsagonyagoodagreeagriaagrinagrosaguedaguesagunaagutiaheadaheapahentahighahindahingahintaholdahullahuruaidasaidedaideraidesaidoiaidosaieryaigasaightailedaimedaimeraineeaingaaioliairedairerairnsairthairtsaisleaitchaitusaiveraiyeeaizleajiesajivaajugaajwanakeesakelaakeneakingakitaakkasalaapalackalamoalandalanealangalansalantalapaalapsalarmalaryalatealaysalbasalbeealbumalcidalcosaldeaalderaldolaleckalecsalefsaleftalephalertalewsaleyealfasalgaealgalalgasalgidalginalgoralgumaliasalibialienalifsalignalikealinealistalivealiyaalkiealkosalkydalkylallayalleeallelalleyallisallodallotallowalloyallylalmahalmasalmehalmesalmudalmugalodsaloedaloesaloftalohaaloinalonealongaloofaloosaloudalowealphaaltaralteralthoaltosalulaalumsalurealvaralwayamahsamainamassamateamautamazeambanamberambitambleambosambryamebaameeramendameneamensamentamiasamiceamiciamideamidoamidsamiesamigaamigoamineaminoaminsamirsamissamityamlasammanammonammosamniaamnicamnioamoksamoleamongamortamouramoveamowtampedampleamplyampulamritamuckamuseamylsananaanataanchoancleanconandroanearaneleanentangasangelangerangleangloangryangstanighanileanilsanimaanimeanimianionaniseankerankhsankleankusanlasannalannasannatannexannoyannulanoasanodeanoleanomyansaeantaeantarantasantedantesanticantisantraantreantsyanuraanvilanyonaortaapaceapageapaidapartapaydapaysapeakapeekapersapertaperyapgaraphidaphisapianapingapiolapishapismapneaapodeapodsapoopaportappalappayappelappleapplyapproappuiappuyapresapronapsesapsisapsosaptedapteraptlyaquaeaquasarabaaraksarameararsarbasarborarcedarchiarcosarcusardebardorardriareadareaearealarearareasarecaareddaredearefyareicarenaarenearepaarerearetearetsarettargalarganargilargleargolargonargotargueargusarhatariasarielarikiarilsariotarisearisharkedarledarlesarmedarmerarmetarmilarmorarnasarnutarobaarohaaroidaromaarosearpasarpenarraharrasarrayarretarrisarrowarrozarsedarsesarseyarsisarsonartalartelarticartisartsyaruhearumsarvalarveearvosarylsasanaasconascotascusasdicashedashenashesashetasideaskedaskeraskewaskoiaskosaspenasperaspicaspieaspisasproassaiassamassayassesassetassezassotasterastirastunasuraaswayaswimasylaatapsataxyatigiatiltatimyatlasatmanatmasatmosatocsatokeatoksatollatomsatomyatoneatonyatopyatriaatripattapattaratticatuasaudadaudioauditaugeraughtauguraulasaulicauloiaulosaumilaunesauntsauntyauraeauralauraraurasaureiauresauricaurisaurumautosauxinavailavaleavantavastavelsavensaversavertavgasavianavineavionaviseavisoavizeavoidavowsavyzeawaitawakeawardawareawarnawashawatoawaveawaysawdlsaweelawetoawfulawingawmryawnedawnerawokeawolsaworkaxelsaxialaxileaxilsaxingaxiomaxionaxiteaxledaxlesaxmanaxmenaxoidaxoneaxonsayahsayayaayelpaygreayinsayontayresayrieazansazideazidoazineazlonazoicazoleazonsazoteazothazukiazureazurnazuryazygyazymeazymsbaaedbaalsbabasbabelbabesbabkababoobabulbabusbaccabaccobaccybachabachsbacksbaconbaddybadgebadlybaelsbaffsbaffybaftsbagelbaggybaghsbagiebahtsbahusbahutbailsbairnbaisabaithbaitsbaizabaizebajanbajrabajribajusbakedbakenbakerbakesbakrabalasbaldsbaldybaledbalerbalesbalksbalkyballsballybalmsbalmybaloobalsabaltibalunbalusbambibanakbanalbancobancsbandabandhbandsbandybanedbanesbangsbaniabanjobanksbannsbantsbantubantybanyabapusbarbebarbsbarbybarcabardebardobardsbardybaredbarerbaresbarfibarfsbargebaricbarksbarkybarmsbarmybarnsbarnybaronbarpsbarrabarrebarrobarrybaryebasalbasanbasedbasenbaserbasesbashobasicbasijbasilbasinbasisbasksbasonbassebassibassobassybastabastebastibastobastsbatchbatedbatesbathebathsbatikbatonbattabattsbattubattybaudsbauksbaulkbaursbavinbawdsbawdybawksbawlsbawnsbawrsbawtybayedbayerbayesbaylebayoubaytsbazarbazoobeachbeadsbeadybeaksbeakybealsbeamsbeamybeanobeansbeanybeardbearebearsbeastbeathbeatsbeatybeausbeautbeauxbebopbecapbeckebecksbedadbedelbedesbedewbedimbedyebeechbeedibeefsbeefybeepsbeersbeerybeetsbefitbefogbegadbeganbegarbegatbegembegetbeginbegotbegumbegunbeigebeigybeingbeinsbekahbelahbelarbelaybelchbeleebelgabeliebellebellsbellybelonbelowbeltsbemadbemasbemixbemudbenchbendsbendybenesbenetbengabenisbennebennibennybentobentsbentybepatberayberesberetbergsberkoberksbermebermsberobberryberthberylbesatbesawbeseebesesbesetbesitbesombesotbestibestsbetasbetedbetelbetesbethsbetidbetonbettabettybevelbeverbevorbevuebevvybewetbewigbezelbezesbezilbezzybhaisbhajibhangbhatsbhelsbhootbhunabhutsbiachbialibialybibbsbibesbiblebiccybicepbicesbiddybidedbiderbidesbidetbidisbidonbieldbiersbiffobiffsbiffybifidbigaebiggsbiggybighabightbiglybigosbigotbijoubikedbikerbikesbikiebilbobilbybiledbilesbilgebilgybilksbillsbillybimahbimasbimbobinalbindibindsbinerbinesbingebingobingsbingybinitbinksbintsbiogsbiomebiontbiotabipedbipodbirchbirdsbirksbirlebirlsbirosbirrsbirsebirsybirthbisesbisksbisombisonbitchbiterbitesbitosbitoubitsybittebittsbittybiviabivvybizesbizzobizzyblabsblackbladebladsbladyblaerblaesblaffblagsblahsblainblameblamsblandblankblareblartblaseblashblastblateblatsblattblaudblawnblawsblaysblazebleakblearbleatblebsblechbleedbleepbleesblendblentblertblessblestbletsbleysblimpblimyblindblingbliniblinkblinsblinyblipsblissblistbliteblitsblitzblivebloatblobsblockblocsblogsblokeblondbloodblookbloombloopbloreblotsblownblowsblowyblubsbludebludsbludybluedbluerbluesbluetblueybluffbluidblumeblunkbluntblurbblursblurtblushblypeboabsboaksboardboarsboartboastboatsbobacbobakbobasbobbybobolbobosboccabocceboccibochebocksbodedbodesbodgebodhibodleboepsboetsboeufboffoboffsboganbogeyboggybogiebogleboguebogusboheabohosboilsboingboinkboitebokedbokehbokesbokosbolarbolasboldsbolesbolixbollsbolosboltsbolusbomasbombebombobombsboncebondsbonedbonerbonesboneybongobongsboniebonksbonnebonnybonusbonzabonzebooaibooayboobsboobyboodybooedboofyboogyboohsbooksbookyboolsboomsboomyboongboonsboordboorsbooseboostboothbootsbootyboozeboozyboppyborakboralborasboraxbordebordsboredboreeborelborerboresborgoboricborksbormsbornaborneboronbortsbortybortzbosiebosksboskybosombosonbossybosunbotasbotchbotelbotesbothybottebottsbottybougeboughbouksbouleboultboundbounsbourdbourgbournbousebousyboutsbovidbowatbowedbowelbowerbowesbowetbowiebowlsbownebowrsbowseboxedboxenboxerboxesboxlaboxtyboyarboyauboyedboyfsboygsboylaboyosboysybozosbraaibracebrachbrackbractbradsbraesbragsbraidbrailbrainbrakebraksbrakybramebrandbranebrankbransbrantbrashbrassbrastbratsbravabravebravibravobrawlbrawnbrawsbraxybraysbrazabrazebreadbreakbreambredebredsbreedbreembreerbreesbreidbreisbremebrensbrentbrerebrersbrevebrewsbreysbriarbribebrickbridebriefbrierbriesbrigsbrikibriksbrillbrimsbrinebringbrinkbrinsbrinybriosbrisebriskbrissbrithbritsbrittbrizebroadbrochbrockbrodsbroghbrogsbroilbrokebromebromobroncbrondbroodbrookbroolbroombroosbrosebrosybrothbrownbrowsbrughbruinbruitbrulebrumebrungbruntbrushbruskbrustbrutebrutsbuatsbuazebubalbubasbubbabubbebubbybubusbuchubuckobucksbuckubudasbuddybudgebudisbudosbuffabuffebuffibuffobuffsbuffybufosbuftybuggybuglebuhlsbuhrsbuiksbuildbuiltbuistbukesbulbsbulgebulgybulksbulkybullabullsbullybulsebumbobumfsbumphbumpsbumpybunasbuncebunchbuncobundebundhbundsbundtbundubundybungsbungybuniabunjebunjybunkobunksbunnsbunnybuntsbuntybunyabuoysbuppyburanburasburbsburdsburetburfiburghburgsburinburkaburkeburksburlsburlyburnsburntburooburpsburqaburroburrsburrybursaburseburstbusbybusedbusesbushybusksbuskybussubustibustsbustybutchbuteobutesbutlebutohbuttebuttsbuttybututbutylbuxombuyerbuzzybwanabwazibydedbydesbykedbykesbylawbyresbyrlsbyssibytesbywaycaaedcabalcabascabbycabercabincablecabobcaboccabrecacaocacascachecackscackycacticaddycadeecadescadetcadgecadgycadiecadiscadrecaecacaesecafescaffscagedcagercagescageycagotcahowcaidscainscairdcairncajoncajuncakedcakescakeycalfscalidcalifcalixcalkscallacallscalmscalmycaloscalpacalpscalvecalyxcamancamascamelcameocamescamiscamoscampicampocampscampycamuscanalcandycanedcanehcanercanescangscanidcannacannscannycanoecanoncansocanstcantocantscantycapascapedcapercapescapexcaphscapizcaplecaponcaposcapotcapricapulcaputcarapcaratcarbocarbscarbycardicardscardycaredcarercarescaretcarexcargocarkscarlecarlscarnscarnycarobcarolcaromcaroncarpicarpscarrscarrycarsecartacartecartscarvecarvycasascascocasedcasescaskscaskycastecastscasuscatchcatercatescattycaudacaukscauldcaulkcaulscaumscaupscauricausacausecavascavedcavelcavercavescaviecavilcawedcawkscaxonceaseceazecebidcecalcecumcedarcededcedercedescedisceibaceiliceilscelebcellacellicellocellscelomceltscensecentocentscentuceorlcepescerciceredcerescergeceriacericcernecerocceroscertscertycessecestacesticetescetylcezvechacechackchacochadochadschafechaffchaftchainchairchaischalkchalschampchamschanachangchankchantchaoschapechapschaptcharachardcharecharkcharmcharrcharschartcharychasechasmchatschavechavschawkchawschayachayscheapcheatcheckcheekcheepcheerchefschekachelachelpchemochemscherechertchesschestchethchevychewschewychiaochiaschibschicachichchickchicochicschidechiefchielchikschildchilechilichillchimbchimechimochimpchinachinechingchinkchinochinschipschirkchirlchirmchirochirpchirrchirtchiruchitschivechivschivychizzchockchocochocschodechogschoilchoirchokechokochokycholacholicholochompchonschoofchookchoomchoonchopschordchorechosechotachottchoutchouxchowkchowschubschuckchufachuffchugschumpchumschunkchurlchurnchurrchusechutechutschylechymechyndcibolcidedcidercidescielscigarciggyciliacillscimarcimexcinchcinctcinescinqscionscippicircacircscirescirlscirriciscocissycistscitalcitedcitercitescivescivetcivicciviecivilcivvyclachclackcladecladsclaesclagsclaimclameclampclamsclangclankclansclapsclaptclaroclartclaryclashclaspclassclastclatsclautclaveclaviclawsclayscleanclearcleatcleckcleekcleepclefscleftclegscleikclemsclepecleptclerkcleveclewsclickcliedcliescliffcliftclimbclimeclineclingclinkclintclipeclipscliptclitscloakcloamclockclodscloffclogsclokeclombclompcloneclonkclonscloopclootclopsclosecloteclothclotscloudclourclouscloutcloveclownclowscloyecloysclozeclubscluckcluedcluesclueyclumpclungclunkclypecnidacoachcoactcoadycoalacoalscoalycoaptcoarbcoastcoatecoaticoatscobbscobbycobiacoblecobracobzacocascoccicoccocockscockycocoacocoscodascodeccodedcodencodercodescodexcodoncoedscoffscogiecogoncoguecohabcohencohoecohogcohoscoifscoigncoilscoinscoirscoitscokedcokescolascolbycoldscoledcolescoleycoliccolincollscollycologcoloncolorcoltscolzacomaecomalcomascombecombicombocombscombycomercomescometcomfycomiccomixcommacommocommscommycompocompscomptcomtecomusconchcondoconedconesconeyconfscongacongecongoconiaconicconinconksconkyconneconnscontecontoconusconvocoochcooedcooeecooercooeycoofscookscookycoolscoolycoombcoomscoomycoonscoopscooptcoostcootscoozecopalcopaycopedcopencopercopescoppycopracopsecopsycoquicoralcoramcorbecorbycordscoredcorercorescoreycorgicoriacorkscorkycormscornicornocornscornucornycorpscorsecorsocoseccosedcosescosetcoseycosiecostacostecostscotancotedcotescothscottacottscouchcoudecoughcouldcountcoupecoupscourbcourdcourecourscourtcoutacouthcovedcovencovercovescovetcoveycovincowalcowancowedcowercowkscowlscowpscowrycoxaecoxalcoxedcoxescoxibcoyaucoyedcoyercoylycoypucozedcozencozescozeycoziecraalcrabscrackcraftcragscraiccraigcrakecramecrampcramscranecrankcranscrapecrapscrapycrarecrashcrasscratecravecrawlcrawscrayscrazecrazycreakcreamcredocredscreedcreekcreelcreepcreescremecremscrenacrepecrepscreptcrepycresscrestcrewecrewscriascribscrickcriedcriercriescrimecrimpcrimscrinecrioscripecripscrisecrispcrithcritscroakcrocicrockcrocscroftcrogscrombcromecronecronkcronscronycrookcroolcrooncropscrorecrosscrostcroupcroutcrowdcrowncrowscrozecruckcrudecrudocrudscrudycruelcruescruetcruftcrumbcrumpcrunkcruorcruracrusecrushcrustcrusycruvecrwthcryercryptctenecubbycubebcubedcubercubescubiccubitcuddycuffocuffscuifscuingcuishcuitscukesculchculetculexcullscullyculmsculpaculticultscultycumeccumincundycuneicunitcuntscupelcupidcuppacuppycuratcurbscurchcurdscurdycuredcurercurescuretcurfscuriacuriecuriocurlicurlscurlycurnscurnycurrscurrycursecursicurstcurvecurvycuseccushycuskscuspscuspycussocusumcutchcutercutescuteycutiecutincutiscuttocuttycutupcuveecuzescwtchcyanocyanscybercycadcycascyclecyclocydercylixcymaecymarcymascymescymolcyniccystscytescytonczarsdaalsdabbadacesdachadacksdadahdadasdaddydadosdaffsdaffydaggadaggydagosdahlsdaikodailydainedaintdairydaisydakerdaleddalesdalisdalledallydaltsdamandamardamesdammedamnsdampsdampydancedancydandydangsdaniodanksdannydantsdarafdarbsdarcydareddarerdaresdargadargsdaricdarisdarksdarkydarnsdarredartsdarzidashidashydataldateddaterdatesdatosdattodatumdaubedaubsdaubydaudsdaultdauntdaursdautsdavendavitdawahdawdsdaweddawendawksdawnsdawtsdayandaychdayntdazeddazerdazesdeadsdeairdealsdealtdeansdearedearndearsdearydeashdeathdeavedeawsdeawydebagdebardebbydebeldebesdebitdebtsdebuddebugdeburdebusdebutdebyedecaddecafdecaldecandecaydeckodecksdecordecosdecoydecrydedaldeedsdeedydeelydeemsdeensdeepsdeeredeersdeetsdeevedeevsdefatdeferdeffodefisdefogdegasdegumdegusdeicedeidsdeifydeigndeilsdeismdeistdeitydekeddekesdekkodelaydeleddelesdelfsdelftdelisdellsdellydelosdelphdeltadeltsdelvedemandemesdemicdemitdemobdemoidemondemosdemptdemurdenardenaydenchdenesdenetdenimdenisdensedentsdeoxydepotdepthderatderayderbyderedderesderigdermadermsdernsdernyderosderroderryderthdervsdesexdeshidesisdesksdessedeterdetoxdeucedevasdeveldevildevisdevondevosdevotdewandewardewaxdeweddexesdexiedhabadhaksdhalsdhikrdhobidholedholldholsdhotidhowsdhutidiactdialsdianediarydiazodibbsdiceddicerdicesdiceydichtdicksdickydicotdictadictsdictydiddydidiedidosdidstdiebsdielsdienedietsdiffsdightdigitdikasdikeddikerdikesdikeydildodillidillsdillydimbodimerdimesdimlydimpsdinardineddinerdinesdingedingodingsdingydinicdinksdinkydinnadinosdintsdiodediolsdiotadippydipsodiramdirerdirgedirkedirksdirlsdirtsdirtydisasdiscidiscodiscsdishydisksdismeditalditasditchditedditesditsydittodittsdittyditzydivandivasdiveddiverdivesdivisdivnadivosdivotdivvydiwandixiedixitdiyasdizendizzydjinndjinsdoabsdoatsdobbydobesdobiedobladobradobrodochtdocksdocosdocusdoddydodgedodgydodosdoeksdoersdoestdoethdoffsdogandogesdogeydoggodoggydogiedogmadohyodoiltdoilydoingdoitsdojosdolcedolcidoleddolesdoliadollsdollydolmadolordolosdoltsdomaldomeddomesdomicdonahdonasdoneedonerdongadongsdonkodonnadonnedonnydonordonsydonutdoobsdoocedoodydooksdooledoolsdoolydoomsdoomydoonadoorndoorsdoozydopasdopeddoperdopesdopeydoraddorbadorbsdoreedoresdoricdorisdorksdorkydormsdormydorpsdorrsdorsadorsedortsdortydosaidosasdoseddosehdoserdosesdoshadotaldoteddoterdotesdottydouardoubtdoucedoucsdoughdouksdouladoumadoumsdoupsdouradousedoutsdoveddovendoverdovesdoviedowardowdsdowdydoweddoweldowerdowiedowledowlsdowlydownadownsdownydowpsdowrydowsedowtsdoxeddoxesdoxiedoyendoylydozeddozendozerdozesdrabsdrackdracodraffdraftdragsdraildraindrakedramadramsdrankdrantdrapedrapsdratsdravedrawldrawndrawsdraysdreaddreamdreardreckdreeddreerdreesdregsdreksdrentdreredressdrestdreysdribsdricedrieddrierdriesdriftdrilldrilydrinkdripsdriptdrivedroiddroildroitdrokedroledrolldromedronedronydroobdroogdrookdrooldroopdropsdroptdrossdroukdrovedrowndrowsdrubsdrugsdruiddrumsdrunkdrupedrusedrusydruxydryaddryasdryerdrylydsobodsomoduadsdualsduansduarsdubboducalducatducesduchyducksduckyductsduddydudeddudesduelsduetsduettduffsdufusduingduitsdukasdukeddukesdukkadulcedulesduliadullsdullydulsedumasdumbodumbsdumkadumkydummydumpsdumpydunamduncedunchdunesdungsdungydunksdunnodunnydunshduntsduomiduomodupedduperdupesdupleduplyduppyduraldurasduredduresdurgydurnsdurocdurosduroydurradurrsdurrydurstdurumdurzidusksduskydustsdustydutchduvetduxesdwaaldwaledwalmdwamsdwangdwarfdwaumdweebdwelldweltdwiledwinedyadsdyersdyingdykeddykesdykeydykondyneldynesdzhoseagereagleeagreealedealeseanedeardsearedearlsearlyearnsearntearsteartheasedeaseleasereaseseasleeastseateneatereatheeavedeavesebbedebbetebonsebonyebookecadsechedechesechoseclatecrusedemaedgededgeredgesedictedifyedileeditseduceeducteejiteensyeerieeeveneevnseffedegadsegersegesteggareggedeggeregmasegretehingeidereidoseighteigneeikedeikoneildseiselejectejidoekingekkaselainelandelanselateelbowelchieldereldinelectelegyelemielfedelfineliadelideelinteliteelmenelogeelogyeloinelopeelopselpeeelsineludeeluteelvanelvenelverelvesemacsemailembarembayembedemberembogembowemboxembusemceeemeeremendemergemeryemeusemicsemirsemitsemmasemmeremmetemmewemmysemojiemongemoteemoveemptsemptyemuleemureemydeemydsenactenarmenateendedenderendewendowendueenemaenemyenewsenfixeniacenjoyenlitenmewennogennuienokienolsenormenowsenrolensewenskyensueenterentiaentryenureenurnenvoienvoyenzymeorlseosinepactepeesephahephasephodephorepicsepochepodeepoptepoxyeprisequalequesequidequiperaseerbiaerecterevsergonergosergoterhusericaerickericseringernederneserodeeroseerrederrorerseseructerugoerupteruvservenervilescarescotesileeskareskeresnesessayessesesterestocestopestroetageetapeetatsetensethaletherethicethneethosethyleticsetnasettinettleetudeetuisetweeetymaeughseukedeupadeuroseusolevadeevenseventeverteveryevetsevhoeevictevilseviteevoheevokeewersewestewhowewkedexactexaltexamsexcelexeatexecsexeemexemeexertexfilexiesexileexineexingexistexitsexodeexomeexonsexpatexpelexposextolextraexudeexulsexultexurbeyasseyerseyingeyotseyraseyreseyrieeyrirezinefabbyfablefacedfacerfacesfacetfaciafactafactsfaddyfadedfaderfadesfadgefadosfaenafaeryfaffsfaffyfaggyfaginfagotfaiksfailsfainefainsfaintfairsfairyfaithfakedfakerfakesfakeyfakiefakirfalajfallsfalsefamedfamesfanalfancyfandsfanesfangafangofangsfanksfannyfanonfanosfanumfaqirfaradfarcefarcifarcyfardsfaredfarerfaresfarlefarlsfarmsfarosfarrofarsefartsfascifastifastsfatalfatedfatesfatlyfatsofattyfatwafaughfauldfaultfaunafaunsfaurdfautsfauvefavasfavelfaverfavesfavorfavusfawnsfawnyfaxedfaxesfayedfayerfaynefayrefazedfazesfealsfearefearsfeartfeasefeastfeatsfeazefecalfecesfechtfecitfecksfedexfeebsfeedsfeelsfeensfeersfeesefeezefehmefeignfeintfeistfelchfelidfellafellsfellyfelonfeltsfeltyfemalfemesfemmefemmyfemurfencefendsfendyfenisfenksfennyfentsfeodsfeoffferalfererferesferiaferlyfermifermsfernsfernyferryfessefestafestsfestyfetalfetasfetchfetedfetesfetidfetorfettafettsfetusfetwafeuarfeudsfeuedfeverfewerfeyedfeyerfeylyfezesfezzyfiarsfiatsfiberfibroficesfichefichuficinficosficusfidesfidgefidosfiefsfieldfiendfientfierefiersfieryfiestfifedfiferfifesfifisfifthfiftyfiggyfightfigosfikedfikesfilarfilchfiledfilerfilesfiletfiliifilksfillefillofillsfillyfilmifilmsfilmyfilosfilthfilumfinalfincafinchfindsfinedfinerfinesfinisfinksfinnyfinosfiordfiqhsfiquefiredfirerfiresfiriefirksfirmsfirnsfirryfirstfirthfiscsfishyfisksfistsfistyfitchfitlyfitnafittefittsfiverfivesfixedfixerfixesfixitfizzyfjeldfjordflabsflackflaffflagsflailflairflakeflaksflakyflameflammflamsflamyflaneflankflansflapsflareflaryflashflaskflatsflavaflawnflawsflawyflaxyflaysfleamfleasfleckfleekfleerfleesfleetflegsflemefleshfleurflewsflexiflexofleysflickflicsfliedflierfliesflimpflimsflingflintflipsflirsflirtfliskfliteflitsflittfloatflobsflockflocsfloesflogsflongfloodfloorflopsfloraflorsfloryfloshflossflotafloteflourfloutflownflowsflubsfluedfluesflueyflufffluidflukeflukyflumeflumpflungflunkfluorflurrflushfluteflutyfluytflybyflyerflypeflytefoalsfoamsfoamyfocalfocusfoehnfogeyfoggyfogiefoglefogoufohnsfoidsfoilsfoinsfoistfoldsfoleyfoliafolicfoliefoliofolksfolkyfollyfomesfondafondsfondufonesfonlyfontsfoodsfoodyfoolsfootsfootyforamforayforbsforbyforcefordofordsforelforesforexforgeforgoforksforkyformeformsforteforthfortsfortyforumforzaforzefossafossefouatfoudsfouerfouetfoulefoulsfoundfountfoursfouthfoveafowlsfowthfoxedfoxesfoxiefoyerfoylefoynefrabsfrackfractfragsfrailfraimframefrancfrankfrapefrapsfrassfratefratifratsfraudfrausfraysfreakfreedfreerfreesfreetfreitfremdfrenafreonfrerefreshfretsfriarfribsfriedfrierfriesfrigsfrillfrisefriskfristfrithfritsfrittfritzfrizefrizzfrockfroesfrogsfrondfronsfrontfrorefrornfroryfroshfrostfrothfrownfrowsfrowyfrozefrugsfruitfrumpfrushfrustfryerfubarfubbyfubsyfucksfucusfuddyfudgefudgyfuelsfuerofuffsfuffyfugalfuggyfugiefugiofuglefuglyfuguefugusfujisfullsfullyfumedfumerfumesfumetfundifundsfundyfungifungofungsfunksfunkyfunnyfuralfuranfurcafurlsfurolfurorfurrsfurryfurthfurzefurzyfusedfuseefuselfusesfusilfusksfussyfustsfustyfutonfuzedfuzeefuzesfuzilfuzzyfycesfykedfykesfylesfyrdsfyttegabbagabbygablegaddigadesgadgegadidgadisgadjegadjogadsogaffegaffsgagedgagergagesgaidsgailygainsgairsgaitagaitsgaittgajosgalahgalasgalaxgaleagaledgalesgallsgallygalopgalutgalvogamasgamaygambagambegambogambsgamedgamergamesgameygamicgamingammagammegammygampsgamutganchgandyganefganevgangsganjaganofgantsgaolsgapedgapergapesgaposgappygarbegarbogarbsgardagaresgarisgarmsgarnigarregarthgarumgasesgaspsgaspygassygastsgatchgatedgatergatesgathsgatorgauchgaucygaudsgaudygaugegaujegaultgaumsgaumygauntgaupsgaursgaussgauzegauzygavelgavotgawcygawdsgawksgawkygawpsgawsygayalgayergaylygazalgazargazedgazergazesgazongazoogealsgeansgearegearsgeatsgeburgeckogecksgeeksgeekygeepsgeesegeestgeistgeitsgeldsgeleegelidgellygeltsgemelgemmagemmygemotgenalgenasgenesgenetgenicgeniegeniigenipgennygenoagenomgenregenrogentsgentygenuagenusgeodegeoidgerahgerbegeresgerlegermsgermygernegessegessogestegestsgetasgetupgeumsgeyangeyerghastghatsghautghazigheesghestghostghoulghyllgiantgibedgibelgibergibesgibligibusgiddygiftsgigasgighegigotgiguegilasgildsgiletgillsgillygilpygiltsgimelgimmegimpsgimpyginchgingegingsginksginnyginzogipongippogippygipsygirdsgirlsgirlygirnsgirongirosgirrsgirshgirthgirtsgismogismsgistsgitchgitesgiustgivedgivengivergivesgizmoglacegladegladsgladyglaikglairglamsglandglansglareglaryglassglaumglaurglazeglazygleamgleanglebaglebeglebygledegledsgleedgleekgleesgleetgleisglensglentgleysglialgliasglibsglidegliffgliftglikeglimeglimsglintgliskglitsglitzgloamgloatglobeglobiglobsglobyglodegloggglomsgloomgloopglopsgloryglossglostgloutgloveglowsglozegluedgluergluesglueyglugsglumeglumsgluongluteglutsglyphgnarlgnarrgnarsgnashgnatsgnawngnawsgnomegnowsgoadsgoafsgoalsgoarygoatsgoatygobangobargobbigobbogobbygobisgobosgodetgodlygodsogoelsgoersgoestgoethgoetygofergoffsgoggagogosgoiergoinggojisgoldsgoldygolemgolesgolfsgollygolpegolpsgombogomergompagonadgonchgonefgonergongsgoniagonifgonksgonnagonofgonysgonzogoobygoodsgoodygooeygoofsgoofygoogsgooksgookygooldgoolsgoolygoonsgoonygoopsgoopygoorsgoorygoosegoosygopakgopikgoralgorasgoredgoresgorgegorisgormsgormygorpsgorsegorsygoshtgossegotchgothsgothygottagouchgougegouksgouragourdgoutsgoutygowangowdsgowfsgowksgowlsgownsgoxesgoyimgoylegraalgrabsgracegradegradsgraffgraftgrailgraingraipgramagramegrampgramsgranagrandgransgrantgrapegraphgrapygraspgrassgrategravegravsgravygraysgrazegreatgrebegrebogrecegreedgreekgreengreesgreetgregegregogreingrensgresegrevegrewsgreysgricegridegridsgriefgriffgriftgrigsgrikegrillgrimegrimygrindgrinsgriotgripegripsgriptgripygrisegristgrisygrithgritsgrizegroangroatgrodygrogsgroingroksgromagronegroofgroomgropegrossgroszgrotsgroufgroupgroutgrovegrovygrowlgrowngrowsgrrlsgrrrlgrubsgruedgruelgruesgrufegruffgrumegrumpgrundgruntgrycegrydegrykegrypegryptguacoguanaguanoguansguardguarsguavagucksguckygudesguessguestguffsgugasguideguidsguildguileguiltguimpguiroguisegulaggulargulasgulchgulesguletgulfsgulfygullsgullygulphgulpsgulpygumbogummagummigummygumpsgundygungegungygunksgunkygunnyguppyguqingurdygurgegurlsgurlygurnsgurrygurshgurusgushyguslaguslegusligussygustogustsgustygutsyguttaguttyguyedguyleguyotguysegwinegyalsgyansgybedgybesgyeldgympsgynaegyniegynnygynosgyozagyposgyppogyppygypsygyralgyredgyresgyrongyrosgyrusgytesgyvedgyveshaafshaarshabithablehabushacekhackshadalhadedhadeshadjihadsthaemshaetshaffshafizhaftshaggshahashaickhaikahaikshaikuhailshailyhainshainthairshairyhaithhajeshajishajjihakamhakashakeahakeshakimhakushalalhaledhalerhaleshalfahalfshalidhallohallshalmahalmshalonhaloshalsehaltshalvahalvehalwahamalhambahamedhameshammyhamzahanaphancehanchhandshandyhangihangshankshankyhansahansehantshaolehaomahapaxhaplyhappihappyhapusharamhardshardyharedharemharesharimharksharlsharmsharnsharosharpsharpyharryharshhartshashyhaskshaspshastahastehastyhatchhatedhaterhateshathahaudshaufshaughhauldhaulmhaulshaulthaunshaunthausehautehavenhaverhaveshavochawedhawkshawmshawsehayedhayerhayeyhaylehazanhazedhazelhazerhazesheadsheadyhealdhealsheameheapsheapyheardhearehearsheartheastheathheatsheaveheavyhebenhebeshechtheckshederhedgehedgyheedsheedyheelsheezehefteheftsheftyheidsheighheilsheirsheisthejabhejraheledhelesheliohelixhellohellshelmsheloshelothelpshelvehemalhemeshemicheminhempshempyhencehenchhendshengehennahennyhenryhentsheparherbsherbyherdsheresherlshermahermshernsheronherosherryhersehertzheryehespshestsheteshethsheuchheughheveahewedhewerhewghhexadhexedhexerhexeshexylheyedhianthickshidedhiderhideshiemshighshighthijabhijrahikedhikerhikeshikoihilarhilchhillohillshillyhiltshilumhilushimbohinauhindshingehingshinkyhinnyhintshioishiplyhippohippyhiredhireehirerhireshissyhistshitchhithehivedhiverhiveshizenhoaedhoagyhoardhoarshoaryhoasthobbyhoboshockshocushodadhodjahoershoganhogenhoggshoghshohedhoickhoiedhoikshoinghoisehoisthokashokedhokeshokeyhokishokkuhokumholdsholedholesholeyholkshollahollohollyholmeholmsholonholosholtshomashomedhomerhomeshomeyhomiehommehomoshonanhondahondshonedhonerhoneshoneyhongihongshonkshonkyhonorhoochhoodshoodyhooeyhoofshookahookshookyhoolyhoonshoopshoordhoorshooshhootshootyhoovehopakhopedhoperhopeshoppyhorahhoralhorashordehorishorkshormehornshornyhorsehorsthorsyhosedhoselhosenhoserhoseshoseyhostahostshotchhotelhotenhotlyhottyhouffhoufshoughhoundhourihourshousehoutshoveahovedhovelhovenhoverhoveshowbehowdyhoweshowffhowfshowkshowlshowrehowsohoxedhoxeshoyashoyedhoylehubbyhuckshudnahududhuershuffshuffyhugerhuggyhuhushuiashulashuleshulkshulkyhullohullshullyhumanhumashumfshumichumidhumorhumphhumpshumpyhumushunchhunkshunkyhuntshurdshurlshurlyhurrahurryhursthurtshushyhuskshuskyhusoshussyhutchhutiahuzzahuzzyhwylshydrahydrohyenahyenshyggehyinghykeshylashyleghyleshylichymenhymnshyndehyoidhypedhyperhypeshyphahyphyhyposhyraxhysonhytheiambiiambsibrikicersichedichesichoriciericilyicingickerickleiconsictalicticictusidantidealideasideesidentidiomidiotidledidleridlesidolaidolsidyllidylsiftarigapoiggediglooiglusihramikansikatsikonsileacilealileumileusiliaciliadilialiliumillerillthimageimagoimamsimariimaumimbarimbedimbueimideimidoimidsimineiminoimmewimmitimmiximpedimpelimpisimplyimpotimproimshiimshyinaneinaptinarminboxinbyeincelincleincogincurincusincutindewindexindiaindieindolindowindriindueineptinerminertinferinfixinfosinfrainganingleingotinioninkedinkerinkleinlayinletinnedinnerinnitinorbinputinruninsetinspointelinterintilintisintraintroinulainureinurninustinvarinwitiodiciodidiodinioniciotasipponiradeirateiridsiringirkedirokoironeironsironyisbasishesisledislesisletisnaeisseiissueistleitchyitemsitheriviediviesivoryixiasixnayixoraixtleizardizarsizzatjaapsjabotjacaljacksjackyjadedjadesjafasjaffajagasjagerjaggsjaggyjagirjagrajailsjakerjakesjakeyjalapjalopjambejambojambsjambujamesjammyjamonjanesjannsjannyjantyjapanjapedjaperjapesjarksjarlsjarpsjartajaruljaseyjaspejaspsjatosjauksjauntjaupsjavasjaveljawanjawedjaxiejazzyjeansjeatsjebeljedisjeelsjeelyjeepsjeersjeezejefesjeffsjehadjehusjelabjellojellsjellyjembejemmyjennyjeonsjeridjerksjerkyjerryjessejestsjesusjetesjetonjettyjeunejewedjeweljewiejhalajiaosjibbajibbsjibedjiberjibesjiffsjiffyjiggyjigotjihadjillsjiltsjimmyjimpyjingojinksjinnejinnijinnsjirdsjirgajirrejismsjivedjiverjivesjiveyjnanajobedjobesjockojocksjockyjocosjodeljoeysjohnsjoinsjointjoistjokedjokerjokesjokeyjokoljoledjolesjollsjollyjoltsjoltyjomonjomosjonesjongsjontyjooksjoramjorumjotasjottyjotunjoualjougsjouksjoulejoursjoustjowarjowedjowlsjowlyjoyedjubasjubesjucosjudasjudgejudgyjudosjugaljugumjuicejuicyjujusjukedjukesjukusjulepjumarjumbojumbyjumpsjumpyjuncojunksjunkyjuntajuntojupesjuponjuraljuratjureljuresjurorjustsjutesjuttyjuvesjuviekaamakababkabarkabobkachakackskadaikadeskadiskafirkagoskaguskahalkaiakkaidskaieskaifskaikakaikskailskaimskaingkainskakaskakiskalamkaleskalifkaliskalpakamaskameskamikkamiskammekanaekanaskandykanehkaneskangakangskanjikantskanzukaonskapaskaphskapokkapowkappakapuskaputkaraskaratkarkskarmakarnskarookaroskarrikarstkarsykartskarzykashakasmekatalkataskatiskattikaughkaurikaurukaurykavalkavaskawaskawaukawedkayakkaylekayoskaziskazookbarskebabkebarkebobkeckskedgekedgykeechkeefskeekskeelskeemakeenokeenskeepskeetskeevekefirkehuakeirskelepkelimkellskellykelpskelpykeltskeltykembokembskempskemptkempykenafkenchkendokenoskentekentskepiskerbskerelkerfskerkykermakernekernskeroskerrykervekesarkestsketasketchketesketolkevelkevilkexeskeyedkeyerkhadikhafskhakikhanskhaphkhatskhayakhazikhedakhethkhetskhojakhorskhoumkhudskiaatkiackkiangkibbekibbikibeikibeskiblakickskickykiddokiddykidelkidgekiefskierskievekievskightkikeskikoikileykilimkillskilnskiloskilpskiltskiltykimbokinaskindakindskindykineskingskininkinkskinkykinoskiorekioskkipeskippakippskirbykirkskirnskirrikisankissykistskitedkiterkiteskithekithskittykitulkivaskiwisklangklapsklettklickkliegkliksklongkloofklugeklutzknackknagsknapsknarlknarsknaurknaveknawekneadkneedkneelkneesknellkneltknifeknishknitskniveknobsknockknollknopsknospknotsknoutknoweknownknowsknubsknurlknurrknursknutskoalakoanskoapskobankoboskoelskoffskoftakogalkohaskohenkohlskoinekojiskokamkokaskokerkokrakokumkolaskoloskombukonbukondokonkskookskookykoorikopekkophskopjekoppakoraikoraskoratkoreskormakoroskorunkoruskoseskotchkotoskotowkourakraalkrabskraftkraiskraitkrangkranskranzkrautkrayskreepkrengkrewekrillkronakronekroonkrubikrunkksarskubiekudoskuduskudzukufiskugelkuiaskukrikukuskulakkulankulaskulfikumiskumyskuriskurrekurtakuruskussokutaskutchkutiskutuskuzuskvasskvellkwelakyackkyakskyangkyarskyatskyboskydstkyleskyliekylinkylixkyloekyndekyndskypeskyriekyteskythelaarilabdalabellabialabislaborlabralacedlacerlaceslacetlaceylacksladdyladedladenladerladesladlelaerslaevolaganlagerlahallaharlaichlaicslaidslaighlaikalaikslairdlairslairylaithlaitylakedlakerlakeslakhslakinlaksalaldylallslamaslambslambylamedlamerlameslamialammylampslanailanaslancelanchlandelandslaneslankslankylantslapellapinlapislapjelapselarchlardslardylareelareslargelargolarislarkslarkylarnslarntlarumlarvalasedlaserlaseslassilassolassulassylastslatahlatchlatedlatenlaterlatexlathelathilathslathylatkelattelatuslauanlauchlaudslaufslaughlaundlauralavallavaslavedlaverlaveslavralavvylawedlawerlawinlawkslawnslawnylaxedlaxerlaxeslaxlylayedlayerlayinlayuplazarlazedlazeslazoslazzilazzoleachleadsleadyleafsleafyleaksleakyleamsleansleantleanyleapsleaptlearelearnlearslearyleaseleashleastleatsleaveleavyleazelebenleccyledesledgeledgyledumleearleechleeksleepsleersleeryleeseleetsleezelefteleftsleftylegallegerlegesleggeleggoleggylegitlehrslehualeirsleishlemanlemedlemellemeslemmalemmelemonlemurlendsleneslengslenislenoslenselentilentoleoneleperlepidlepraleptaleredlereslerpslesboleseslestsletchletheletupleuchleucoleudsleughlevasleveelevelleverleveslevinlevislewislexeslexislezeslezzalezzylianalianeliangliardliarsliartlibelliberlibralibrilichilichtlicitlickslidarlidosliefsliegelienslierslieuslieveliferlifesliftsliganligerliggelightlignelikedlikenlikerlikeslikinlilaclillslilosliltslimanlimaslimaxlimbalimbilimbolimbslimbylimedlimenlimeslimeylimitlimmalimnslimoslimpalimpslinaclinchlindslindylinedlinenlinerlineslineylingalingolingslingylininlinkslinkylinnslinnylinoslintslintylinumlinuxlionslipaslipeslipidlipinliposlippyliraslirkslirotliskslislelispslistslitailitaslitedliterliteslithelitholithslitrelivedlivenliverliveslividlivorlivrellamallanoloachloadsloafsloamsloamyloansloastloathloavelobarlobbylobedlobesloboslobuslocallochelochslocielocislockslocoslocumlocuslodenlodeslodgeloessloftsloftyloganlogesloggylogialogiclogieloginlogoilogonlogoslohanloidsloinsloipeloirslokeslollslollylologlomaslomedlomeslonerlongalongelongsloobylooedlooeyloofaloofslooielookslookyloomsloonsloonyloopsloopyloordlooselootslopedloperlopesloppyloralloranlordslordylorelloresloriclorislorrylosedlosellosenloserloseslossylotahlotaslotesloticlotoslotsalottalottelottolotuslouedloughlouielouisloumaloundlounsloupeloupslourelourslourylouselousyloutslovatlovedloverlovesloveylovielowanlowedlowerloweslowlylowndlownelownslowpslowrylowselowtsloxedloxesloyallozenluachluauslubedlubeslubraluceslucidlucksluckylucreludesludicludosluffaluffslugedlugerlugeslullsluluslumaslumbilumenlummelummylumpslumpylunarlunaslunchluneslunetlungelungilungslunksluntslupinlupuslurchluredlurerlureslurexlurgilurgyluridlurkslurrylurveluserlushyluskslustslustylususlutealutedluterlutesluvvyluxedluxerluxeslweislyamslyardlyartlyaselycealyceelycralyinglymeslymphlyneslyreslyriclysedlyseslysinlysislysollyssalytedlyteslythelyticlyttamaaedmaaremaarsmabesmacasmacawmacedmacermacesmachemachimachomachsmacksmaclemaconmacromadammadgemadidmadlymadremaerlmafiamaficmagesmaggsmagicmagmamagotmagusmahoemahuamahwamaidsmaikomaiksmailemaillmailsmaimsmainsmairemairsmaisemaistmaizemajormakarmakermakesmakismakosmalammalarmalasmalaxmalesmalicmalikmalismallsmalmsmalmymaltsmaltymalusmalvamalwamamasmambamambomameemameymamiemammamammymanasmanatmandimanebmanedmanehmanesmanetmangamangemangomangsmangymaniamanicmanismankymanlymannamanormanosmansemantamantomantymanulmanusmapaumaplemaquimaraemarahmarasmarchmarcsmardymaresmargemargsmariamaridmarkamarksmarlemarlsmarlymarmsmaronmarormarramarrimarrymarsemarshmartsmarvymasasmasedmasermasesmashymasksmasonmassamassemassymastsmastymasusmataimatchmatedmatermatesmateymathsmatinmatlomattemattsmatzamatzomaubymaudsmaulsmaundmaurimausymautsmauvemauzymavenmaviemavinmavismawedmawksmawkymawnsmawrsmaxedmaxesmaximmaxismayanmayasmaybemayedmayormayosmaystmazedmazermazesmazeymazutmbirameadsmealsmealymeanemeansmeantmeanymearemeasemeathmeatsmeatymebosmeccamechsmecksmedalmediamedicmediimedlemeedsmeersmeetsmeffsmeinsmeintmeinymeithmekkamelasmelbameldsmeleemelicmelikmellsmelonmeltsmeltymemesmemosmenadmendsmenedmenesmengemengsmensamensemenshmentamentomenusmeousmeowsmerchmercsmercymerdemeredmerelmerermeresmergemerilmerismeritmerksmerlemerlsmerrymersemesalmesasmeselmesesmeshymesicmesnemesonmessymestometalmetedmetermetesmethomethsmeticmetifmetismetolmetremetromeusemevedmevesmewedmewlsmeyntmezesmezzemezzomhorrmiaoumiaowmiasmmiaulmicasmichemichtmicksmickymicosmicramicromiddymidgemidgymidismidstmiensmievemiffsmiffymiftymiggsmightmihasmihismikedmikesmikramikvamilchmildsmilermilesmilfsmiliamilkomilksmilkymillemillsmilormilosmilpamiltsmiltymiltzmimedmimeomimermimesmimicmimsyminaeminarminasmincemincymindsminedminerminesmingemingsmingyminimminisminkeminksminnyminorminosmintsmintyminusmiredmiresmirexmiridmirinmirksmirkymirlymirosmirthmirvsmirzamischmisdomisermisesmisgomisosmissamissymistsmistymitchmitermitesmitismitremittsmixedmixenmixermixesmixtemixupmizenmizzymnememoansmoatsmobbymobesmobeymobiemoblemochamochimochsmochymocksmodalmodelmodemmodermodesmodgemodiimodusmoersmofosmoggymogulmohelmohosmohrsmohuamohurmoilemoilsmoiramoiremoistmoitsmojosmokesmokismokosmolalmolarmolasmoldsmoldymoledmolesmollamollsmollymoltomoltsmolysmomesmommamommymomusmonadmonalmonasmondemondomonermoneymongomongsmonicmoniemonksmonosmontemonthmontymoobsmoochmoodsmoodymooedmooksmoolamoolimoolsmoolymoongmoonsmoonymoopsmoorsmoorymoosemootsmoovemopedmopermopesmopeymoppymopsymopusmoraemoralmorasmoratmoraymorelmoresmoriamornemornsmoronmorphmorramorromorsemortsmosedmosesmoseymosksmossomossymostemostsmotedmotelmotenmotesmotetmoteymothsmothymotifmotismotormottemottomottsmottymotusmotzamouchmouesmouldmoulsmoultmoundmountmoupsmournmousemoustmousymouthmovedmovermovesmoviemowasmowedmowermowramoxasmoxiemoyasmoylemoylsmozedmozesmozosmpretmuchomucicmucidmucinmucksmuckymucormucromucusmuddymudgemudirmudramuffsmuftimuggamuggsmuggymuhlymuidsmuilsmuirsmuistmujikmulchmulctmuledmulesmuleymulgamuliemullamullsmulsemulshmummsmummymumpsmumsymumusmunchmungamungemungomungsmunismuntsmuntumuonsmuralmurasmuredmuresmurexmuridmurksmurkymurlsmurlymurramurremurrimurrsmurrymurtimurvamusarmuscamusedmusermusesmusetmushamushymusicmusitmusksmuskymusosmussemussymusthmustsmustymutchmutedmutermutesmuthamutismutonmuttsmuxedmuxesmuzakmuzzymvulemyallmylarmynahmynasmyoidmyomamyopemyopsmyopymyrrhmysidmythimythsmythymyxosmzeesnaamsnaansnabesnabisnabksnablanabobnachenachonacrenadasnadirnaevenaevinaffsnagasnaggynagornahalnaiadnaifsnaiksnailsnairanairunaivenakednakernakfanalasnalednallanamednamernamesnammanamusnanasnancenancynandunannanannynanosnanuanapasnapednapesnapoonappanappenappynarasnarconarcsnardsnaresnaricnarisnarksnarkynarrenasalnashinastynatalnatchnatesnatisnattynauchnauntnavalnavarnavelnavesnavewnavvynawabnazesnazirnazisndujaneafenealsneapsnearsneathneatsnebeknebelnecksneddyneedsneedyneeldneeleneembneemsneepsneeseneezenegronegusneifsneighneistneivenelisnellynemasnemnsnemptnenesneonsnepernepitneralnerdsnerdynerkanerksnerolnertsnertznervenervynestsnetesnetopnettsnettyneuksneumeneumsnevelnevernevesnevusnewbsnewednewelnewernewienewlynewsynewtsnextsnexusngaionganangatingomangweenicadnicernichenichtnicksnicolnidalnidednidesnidornidusnieceniefsnievenifesniffsniffyniftynigernighsnightnihilnikabnikahnikaunillsnimbinimbsnimpsninerninesninjaninnyninonninthnipasnippyniqabnirlsnirlyniseinissenisusniternitesnitidnitonnitrenitronitrynittynivalnixednixernixesnixienizamnkosinoahsnobbynoblenoblynocksnodalnoddynodesnodusnoelsnoggsnohownoilsnoilynointnoirsnoisenoisynolesnollsnolosnomadnomasnomennomesnomicnomoinomosnonasnoncenonesnonetnongsnonisnonnynonylnoobsnooitnooksnookynoonsnoopsnoosenopalnorianorisnorksnormanormsnorthnosednosernosesnoseynotalnotchnotednoternotesnotumnouldnoulenoulsnounsnounynoupsnovaenovasnovelnovumnowaynowednowlsnowtsnowtynoxalnoxesnoyaunoyednoyesnubbynubianuchanuddynudernudesnudgenudienudzhnuffsnugaenukednukesnullanullsnumbsnumennummynunnynurdsnurdynurlsnurrsnursenutsonutsynuttynyaffnyalanyingnylonnymphnyssaoakedoakenoakeroakumoaredoasesoasisoastsoatenoateroathsoavesobangobeahobeliobeseobeysobiasobiedobiitobitsobjetoboesoboleoboliobolsoccamoccuroceanocherochesochreochryockerocreaoctadoctaloctanoctasoctetoctyloculiodahsodalsodderoddlyodeonodeumodismodistodiumodorsodourodyleodylsofaysoffaloffedofferoffieoflagoftenofterogamsogeedogeesogginoghamogiveogledogleroglesogmicogresohiasohingohmicohoneoidiaoiledoileroinksointsojimeokapiokaysokehsokrasoktasoldenolderoldieoleicoleinolentoleosoleumoliosoliveollasollavollerollieologyolpaeolpesomasaomberombreombusomegaomensomersomitsomlahomovsomrahonceroncesoncetoncusonelyonersoneryoniononiumonkusonlayonnedonsetonticoobitoohedoomphoontsoopedoorieoosesootidoozedoozesopahsopalsopensopepeoperaopineopingopiumopposopsinoptedopteropticorachoracyoralsorangorantorateorbedorbitorcasorcinorderordosoreadorfesorganorgiaorgicorgueoribiorielorixaorlesorlonorlopormerornisorpinorrisorthoorvalorzososcaroshacosierosmicosmolossiaostiaotakuotaryotherottarotterottosoubitouchtouensoughtouijaoulksoumasounceoundyoupasoupedoupheouphsourieouseloustsoutbyoutdooutedouteroutgooutreoutroouttaouzelouzosovalsovaryovateovelsovensoversovertovineovistovoidovoliovoloovuleowcheowiesowingowledowlerowletownedownerowresowrieowsenoxbowoxersoxeyeoxideoxidsoxiesoximeoximsoxlipoxteroyersozekiozoneozziepaalspaanspacaspacedpacerpacespaceypachapackspacospactapactspaddypadispadlepadmapadrepadripaeanpaedopaeonpaganpagedpagerpagespaglepagodpagripaikspailspainspaintpairepairspaisapaisepakkapalaspalaypaleapaledpalerpalespaletpalispalkipallapallspallypalmspalmypalpipalpspalsapalsypampapanaxpancepandapandspandypanedpanelpanespangapangspanicpanimpankopannepannipansypantopantspantypaolipaolopapalpapaspapawpaperpapespappipappyparaeparasparchpardipardspardyparedparenpareoparerparespareuparevpargepargoparisparkaparkiparksparkyparleparlyparmaparolparpsparraparrsparryparsepartipartspartyparveparvopaseopasespashapashmpaskapaspypassepastapastepastspastypatchpatedpatenpaterpatespathspatinpatiopatkapatlypatsypattepattypatuspauaspaulspausepavanpavedpavenpaverpavespavidpavinpavispawaspawawpawedpawerpawkspawkypawlspawnspaxespayedpayeepayerpayorpaysdpeacepeachpeagepeagspeakspeakypealspeanspearepearlpearspeartpeasepeatspeatypeavypeazepebaspecanpechspeckepeckspeckypedalpedespedispedropeecepeekspeelspeenspeeoypeepepeepspeerspeerypeevepeggypeghspeinspeisepeizepekanpekespekinpekoepelaspelaupelespelfspellspelmapelonpeltapeltspenalpencependspendupenedpenespengopeniepenispenkspennapennepennipennypentspeonspeonypeplapepospeppypepsiperaiperceperchpercsperduperdypereaperesperilperisperksperkypermspernsperogperpsperryperseperstpertspervepervopervspervypeskypesospestopestspestypetalpetarpeterpetitpetrepetripettipettopettypeweepewitpeysephagephangpharepharmphasepheerphenepheonphesephialphishphizzphloxphocaphonephonophonsphonyphotophotsphphtphutsphylaphylepianipianopianspibalpicalpicaspiccypickspickypicotpicrapiculpiecepiendpierspiertpietapietspietypiezopiggypightpigmypiingpikaspikaupikedpikerpikespikeypikispikulpilaepilafpilaopilarpilaupilawpilchpileapiledpileipilerpilespilispillspilotpilowpilumpiluspimaspimpspinaspinchpinedpinespineypingopingspinkopinkspinkypinnapinnypinonpinotpintapintopintspinuppionspionypiouspioyepioyspipalpipaspipedpiperpipespipetpipispipitpippypipulpiquepiraipirlspirnspirogpiscopisespiskypisospissypistepitaspitchpithspithypitonpitotpittapiumspivotpixelpixespixiepizedpizespizzaplaasplaceplackplageplaidplainplaitplaneplankplansplantplapsplashplasmplastplateplatsplattplatyplayaplaysplazapleadpleaspleatplebeplebsplenapleonpleshplewsplicapliedplierpliesplimsplingplinkploatplodsplongplonkplookplopsplotsplotzploukplowsployeployspluckpluespluffplugsplumbplumeplumpplumsplumyplunkpluotplushplutoplyerpoachpoakapoakepoboypockspockypodalpoddypodexpodgepodgypodiapoemspoepspoesypoetspogeypoggepogospohedpoilupoindpointpoisepokalpokedpokerpokespokeypokiepolarpoledpolerpolespoleypoliopolispoljepolkapolkspollspollypolospoltspolyppolyspombepomespommypomospompsponceponcypondsponesponeypongapongopongspongyponkspontspontyponzupoochpoodspooedpoofspoofypoohspoojapookapookspoolspoonspoopspoopypooripoortpootspoovepoovypopespoppapoppypopsyporaeporalporchporedporerporesporgeporgyporinporksporkypornopornspornyportaportsportyposedposerposesposeyposhopositpossepostspotaepotchpotedpotespotinpotoopotsypottopottspottypouchpouffpoufspoukepoukspoulepoulppoultpoundpoupepouptpourspoutspoutypowanpowerpowinpowndpownspownypowrepoxedpoxespoyntpoyoupoysepozzypraampradsprahupramspranaprangprankpraosprasepratepratsprattpratyprausprawnprayspredypreedpreenpreespreifpremspremyprentpreonpreopprepspresapresepressprestpreveprexypreysprialpriceprickpricypridepriedpriefprierpriesprigsprillprimaprimeprimiprimoprimpprimsprimyprinkprintprionpriorpriseprismprissprivyprizeproasprobeprobsprodsproemprofsprogsproinprokeproleprollpromopromsproneprongpronkproofpropsproreproseprosoprossprostprosyprotoproudproulproveprowlprowsproxyproynprudeprunepruntprutapryerprysepsalmpseudpshawpsionpsoaepsoaipsoaspsorapsychpsyoppubcopubespubicpubispucanpucerpucespuckapuckspuddypudgepudgypudicpudorpudsypuduspuerspuffapuffspuffypuggypugilpuhaspujahpujaspukaspukedpukerpukespukeypukkapukuspulaopulaspuledpulerpulespulikpulispulkapulkspullipullspullypulmopulpspulpypulsepuluspumaspumiepumpspunaspuncepunchpungapungspunjipunkapunkspunkypunnypuntopuntspuntypupaepupaspupilpuppypupuspurdapuredpureepurerpurespurgepurinpurispurlspurpypurrspursepursypurtypusespushypuslepussyputidputonputtiputtoputtsputtypuzelpwnedpyatspyetspygalpygmypyinspylonpynedpynespyoidpyotspyralpyranpyrespyrexpyricpyrospyxedpyxespyxiepyxispzazzqadisqaidsqajaqqanatqapikqiblaqophsqormaquackquadsquaffquagsquailquairquaisquakequakyqualequalmquantquarequarkquartquashquasiquassquatequatsquaydquaysqubitqueanqueenqueerquellquemequenaquernqueryquestqueuequeynqueysquichquickquidsquietquiffquillquiltquimsquinaquinequinoquinsquintquipoquipsquipuquirequirkquirtquistquitequitsquoadquodsquoifquoinquoitquollquonkquopsquotaquotequothqurshquyterabatrabbirabicrabidrabisracedracerracesracheracksraconradarradgeradiiradioradixradonraffsraftsragasragderagedrageeragerragesraggaraggsraggyragisragusrahedrahuiraiasraidsraiksrailerailsrainerainsrainyrairdraiseraitaraitsrajahrajasrajesrakedrakeerakerrakesrakiarakisrakusralesrallyralphramalrameeramenrametramieraminramisrammyrampsramusranasranceranchrandsrandyraneerangarangerangirangsrangyranidranisrankeranksrantsrapedraperrapesrapherapidrapperaredrareerarerraresrarksrasedraserrasesraspsraspyrasserastaratalratanratasratchratedratelraterratesratharatherathsratioratooratosrattyratusraunsrauporavedravelravenraverravesraveyravinrawerrawinrawlyrawnsraxedraxesrayahrayasrayedrayleraynerayonrazedrazeerazerrazesrazoorazorreachreactreaddreadsreadyreaisreaksrealmrealorealsreamereamsreamyreansreapsrearmrearsreastreatareatereaverebarrebberebecrebelrebidrebitreboprebusrebutrebuyrecalrecapreccereccoreccyrecitrecksreconrectarectirectorecurrecutredanreddsreddyrededredesrediaredidredipredlyredonredosredoxredryredubreduxredyereechreedereedsreedyreefsreefyreeksreekyreelsreensreestreeverefedrefelreferrefforefisrefitrefixreflyrefryregalregarregesreggoregieregmaregnaregosregurrehabrehemreifsreifyreignreikireiksreinkreinsreirdreistreiverejigrejonrekedrekesrekeyrelaxrelayreletrelicrelierelitrelloremanremapremenremetremexremitremixrenalrenayrendsrenewreneyrengarenigreninrennerenosrenterentsreoilreorgrepayrepegrepelrepinreplareplyreposrepotreppsreproreranrerigrerunresatresawresayreseeresesresetresewresidresinresitresodresowrestorestsrestyresusretagretaxretchretemretiaretieretoxretroretryreuserevelrevetrevierevuerewanrewaxrewedrewetrewinrewonrewthrexesrezesrheasrhemerheumrhiesrhimerhinerhinorhodyrhombrhonerhumbrhymerhynerhytariadsrialsriantriataribasribbyribesricedricerricesriceyrichtricinricksriderridesridgeridgyridicrielsriemsrieveriferriffsriflerifteriftsriftyriggsrightrigidrigolrigorriledrilesrileyrillerillsrimaerimedrimerrimesrimusrindsrindyrinesringsrinksrinseriojariotsripedripenriperripesrippsrisenriserrisesrishirisksriskyrispsrisusritesrittsritzyrivalrivasrivedrivelrivenriverrivesrivetriyalrizasroachroadsroamsroansroarsroaryroastroaterobedrobesrobinroblerobotrocksrockyrodedrodeorodesrogerrogueroguyrohesroidsroilsroilyroinsroistrojakrojisrokedrokerrokesrolagrolesrolfsrollsromalromanromeorompsronderondoroneoronesroninronneronterontsroodsroofsroofyrooksrookyroomsroomyroonsroopsroopyroosarooseroostrootsrootyropedroperropesropeyroqueroralroresroricroridrorierortsrortyrosedrosesrosetroshirosinrositrostirostsrotalrotanrotasrotchrotedrotesrotisrotlsrotonrotorrotosrotterouenrouesrougeroughrouleroulsroumsroundroupsroupyrouseroustrouterouthroutsrovedrovenroverrovesrowanrowdyrowedrowelrowenrowerrowierowmerowndrowthrowtsroyalroyneroystrozetrozitruanarubairubbyrubelrubesrubinrublerublirubusrucherucksrudasruddsruddyruderrudesrudierudisruedaruersrufferuffsrugaerugalrugbyruggyruingruinsrukhsruledrulerrulesrumalrumbarumborumenrumesrumlyrummyrumorrumporumpsrumpyrunchrundsrunedrunesrungsrunicrunnyruntsruntyrupeerupiaruralrurpsrurusrusasrusesrushyrusksrusmarusserustsrustyruthsrutinruttyryalsrybatrykedrykesrymmeryndsryotsrypersaagssabalsabedsabersabessabhasabinsabirsablesabotsabrasabresackssacrasaddosadessadhesadhusadissadlysadossadzasafedsafersafessagassagersagessaggysagossagumsahebsahibsaicesaicksaicssaidssaigasailssaimssainesainssaintsairssaistsaithsajousakaisakersakessakiasakissaktisaladsalalsalatsalepsalessaletsalicsalixsallesallysalmisalolsalonsalopsalpasalpssalsasalsesaltosaltssaltysaluesalutsalvesalvosamansamassambasambosameksamelsamensamessameysamfusammysampisampssandssandysanedsanersanessangasanghsangosangssankosansasantosantssaolasapansapidsaporsappysaransardssaredsareesargesargosarinsarissarkssarkysarodsarossarussasersasinsassesassysataisataysatedsatemsatessatinsatissatyrsaubasaucesauchsaucysaughsaulssaultsaunasauntsaurysautesautssavedsaversavessaveysavinsavorsavoysavvysawahsawedsawersaxessayedsayersayidsaynesayonsaystsazesscabsscadsscaffscagsscailscalascaldscalescallscalpscalyscampscamsscandscansscantscapascapescapiscarescarfscarpscarsscartscaryscathscatsscattscaudscaupscaurscawssceatscenascendscenescentschavschmoschulschwascionsclimscodyscoffscogsscoldsconescoogscoopscootscopascopescopsscorescornscotsscougscoupscourscoutscowlscowpscowsscrabscraescragscramscranscrapscratscrawscrayscreescrewscrimscripscrobscrodscrogscrowscrubscrumscubascudiscudoscudsscuffscuftscugssculkscullsculpsculsscumsscupsscurfscursscusescutascutescutsscuzzscyessdaynsdeinsealsseameseamsseamyseanssearesearsseaseseatsseazesebumseccosechssectssedansedersedessedgesedgysedumseedsseedyseeksseeldseelsseelyseemsseepsseepyseerssefersegarsegnisegnosegolsegosseguesehriseifsseilsseineseirsseiseseismseityseizaseizesekossektsselahselesselfssellasellesellsselvasemeesemensemessemiesemissenassendssenessengisennasenorsensasensesensisentesentisentssenvysenzasepadsepalsepiasepicsepoyseptaseptsseracseraiseralseredsererseresserfssergesericserifserinserksseronserowserraserreserrsserryserumserveservoseseysessasetaesetalsetonsettssetupsevenseversewansewarsewedsewelsewensewersewinsexedsexersexessextosextsseyenshackshadeshadsshadyshaftshagsshahsshakeshakoshaktshakyshaleshallshalmshaltshalyshamashameshamsshandshankshansshapeshapsshardsharesharksharnsharpshashshaulshaveshawlshawmshawnshawsshayashaysshchisheafshealshearsheasshedssheelsheensheepsheersheetsheikshelfshellshendshentsheolsherdsheresheroshetsshevashewnshewsshiaishiedshielshiershiesshiftshillshilyshimsshineshinsshinyshipsshireshirkshirrshirsshirtshishshisoshistshiteshitsshiurshivashiveshivsshlepshlubshmekshmoeshoalshoatshockshoedshoershoesshogishogsshojishojosholashoneshookshoolshoonshoosshootshopeshopsshoreshorlshornshortshoteshotsshottshoutshoveshowdshownshowsshowyshoyushredshrewshrisshrowshrubshrugshtikshtumshtupshuckshuleshulnshulsshunsshuntshurashushshuteshutsshwasshyershylysialssibbssibylsicessichtsickosickssickysidassidedsidersidessidhasidhesidlesiegesieldsienssientsiethsieursievesiftssighssightsigilsiglasigmasignasignssijossikassikersikessildssiledsilensilersilessilexsilkssilkysillssillysilossiltssiltysilvasimarsimassimbasimissimpssimulsincesindssinedsinessinewsingesingssinhssinkssinkysinussipedsipessippysiredsireesirensiressirihsirissirocsirrasirupsisalsisessissysistasistssitarsitedsitessithesitkasitupsitussiversixersixessixmosixtesixthsixtysizarsizedsizelsizersizesskagsskailskaldskankskartskateskatsskattskawsskeanskearskedsskeedskeefskeenskeerskeesskeetskeggskegsskeinskelfskellskelmskelpskeneskensskeosskepsskerssketsskewsskidsskiedskierskiesskieyskiffskillskimoskimpskimsskinkskinsskintskiosskipsskirlskirrskirtskiteskitsskiveskivysklimskoalskodyskoffskogsskolsskoolskortskoshskranskrikskuasskugsskulkskullskunkskyedskyerskyeyskyfsskyreskyrsskyteslabsslacksladeslaesslagsslaidslainslakeslamsslaneslangslankslantslapsslartslashslateslatsslatyslawsslaysslebssledssleeksleepsleersleetsleptslewssleyssliceslickslideslierslilyslimeslimsslimyslingslinkslipeslipssliptslishslitsslivesloanslobssloesslogssloidslojdslomosloomsloopslootslopeslopsslopyslormsloshslothslotssloveslowssloydslubbslubssluedsluessluffslugssluitslumpslumsslungslunkslurbslurpslurssluseslushslutsslyerslylyslypesmaaksmacksmaiksmallsmalmsmaltsmarmsmartsmashsmazesmearsmeeksmeessmeiksmekesmellsmeltsmerksmewssmilesmirksmirrsmirssmitesmithsmitssmocksmogssmokesmokosmokysmoltsmoorsmootsmoresmorgsmotesmoutsmowtsmugssmurssmushsmutssnabssnacksnafusnagssnailsnakesnakysnapssnaresnarfsnarksnarlsnarssnarysnashsnathsnawssneadsneaksneapsnebssnecksnedssneedsneersneessnellsnibssnicksnidesniessniffsniftsnigssnipesnipssnipysnirtsnitssnobssnodssnoeksnoepsnogssnokesnoodsnooksnoolsnoopsnootsnoresnortsnotssnoutsnowksnowssnowysnubssnucksnuffsnugssnushsnyessoakssoapssoapysoaresoarssoavesobassobersocassocessockosockssoclesodassoddysodicsodomsofarsofassoftasoftssoftysogersoggysohursoilssoilysojassojussokahsokensokessokolsolahsolansolarsolassoldesoldisoldosoldssoledsoleisolersolessolidsolonsolossolumsolussolvesomansomassonarsoncesondesonessongssonicsonlysonnesonnysonsesonsysooeysookssookysoolesoolssoomssoopssootesoothsootssootysophssophysoporsoppysoprasoralsorassorbosorbssordasordosordssoredsoreesorelsorersoressorexsorgosornssorrasorrysortasortssorussothssotolsoucesouctsoughsoukssoulssoumssoundsoupssoupysourssousesouthsoutssowarsowcesowedsowersowffsowfssowlesowlssowmssowndsownesowpssowsesowthsoyassoylesoyuzsozinspacespacyspadespadospaedspaerspaesspagsspahispailspainspaitspakespaldspalespallspaltspamsspanespangspankspansspardsparesparksparsspartspasmspatespatsspaulspawlspawnspawsspaydspaysspazaspazzspeakspealspeanspearspeatspeckspecsspectspeedspeelspeerspeilspeirspeksspeldspelkspellspeltspendspentspeosspermspetsspeugspewsspewyspialspicaspicespickspicsspicyspidespiedspielspierspiesspiffspifsspikespiksspikyspilespillspiltspimsspinaspinespinkspinsspinyspirespirtspiryspitespitsspitzspivssplatsplaysplitsplogspodespodsspoilspokespoofspookspoolspoomspoonspoorspootsporesporksportsposhspotsspoutspradspragspratsprayspredspreesprewsprigspritsprodsprogspruesprugspudsspuedspuerspuesspugsspulespumespumyspunkspurnspursspurtsputaspyalspyresquabsquadsquatsquawsquegsquibsquidsquitsquizstabsstackstadestaffstagestagsstagystaidstaigstainstairstakestalestalkstallstampstandstanestangstankstaphstapsstarestarkstarnstarrstarsstartstashstatestatsstaunstavestawsstayssteadsteakstealsteamsteanstearsteddstedestedssteedsteeksteelsteemsteensteepsteersteilsteinstelastelestellstemestemsstendstenostensstentstepssteptsteresternstetsstewsstewysteysstichstickstiedstiesstiffstilbstilestillstiltstimestimsstimystingstinkstintstipastipestirestirkstirpstirsstivestivystoaestoaistoasstoatstobsstockstoepstogystoicstoitstokestolestolnstomastompstondstonestongstonkstonnstonystoodstookstoolstoopstoorstopestopsstoptstorestorkstormstorystossstotsstottstounstoupstourstoutstovestownstowpstowsstradstraestragstrakstrapstrawstraystrepstrewstriastrigstrimstripstropstrowstroystrumstrutstubsstuckstudestudsstudystuffstullstulmstummstumpstumsstungstunkstunsstuntstupastupesturesturtstyedstyesstylestylistylostymestymystyrestytesuavesubahsubassubbysubersubhasuccisuckssuckysucresuddssudorsudsysuedesuentsuerssuetesuetssuetysugansugarsughssugossuhursuidssuingsuintsuitesuitssujeesukhssukuksulcisulfasulfosulkssulkysullysulphsulussumacsumissummasumossumphsumpssunissunkssunnasunnssunnysunupsupersupessuprasurahsuralsurassuratsurdssuredsurersuressurfssurfysurgesurgysurlysurrasusedsusessushisusussutorsutrasuttaswabsswackswadsswageswagsswailswainswaleswalyswamiswampswamyswangswankswansswapsswaptswardswareswarfswarmswartswashswathswatsswaylswaysswealswearsweatswedesweedsweelsweepsweersweessweetsweirswellsweltsweptswerfsweysswiesswiftswigsswileswillswimsswineswingswinkswipeswireswirlswishswissswithswitsswiveswizzswobsswoleswolnswoonswoopswopsswoptswordsworeswornswotsswounswungsybbesybilsyboesybowsyceesycessyconsyenssykersykessylissylphsylvasymarsynchsyncssyndssynedsynessynodsynthsypedsypessyphssyrahsyrensyrupsysopsythesyvertaalstaatatabbytabertabestabidtabistablatabletabootabortabuntabustacantacestacettachetachotachstacittackstackytacostactstaelstaffytafiataggytagmatahastahrstaigataigstaikotailstainstainttairataishtaitstajestakastakentakertakestakhitakintakistakkytalaktalaqtalartalastalcstalcytaleatalertalestalkstalkytallstallytalmatalontalpataluktalustamaltamedtamertamestamintamistammytampstanastangatangitangotangstangytanhstankatankstankytannatansytantitantotantytapastapedtapentapertapestapettapirtapistappatapustarastardotardytaredtarestargatargetarnstaroctaroktarostarottarpstarretarrytarsitartstartytasartasedtasertasestaskstassatassetassotastetastytatartatertatestathstatietatoutattstattytatustaubetauldtaunttauontaupetautstavahtavastavertawaitawastawedtawertawietawnytawsetawtstaxedtaxertaxestaxistaxoltaxontaxortaxustayratazzatazzeteachteadeteadsteaedteakstealsteamstearstearyteaseteatsteazetechstechytectateddyteelsteemsteendteeneteensteenyteersteethteffsteggsteguategustehrsteiidteilsteindteinstelaetelcotelestelexteliatelictellstellyteloitelostemedtemestempitempotempstempttemsetenchtendstendutenestenettengeteniatennetennotennytenontenortensetenthtentstentytenuetepaltepastepeetepidtepoyteraiterasterceterekteresterfeterfstergatermsterneternsterraterrytersetertsteslatestatesteteststestytetestethstetratetriteuchteughtewedteweltewittexastexestextsthackthagithaimthalethalithanathanethangthankthansthanxtharmtharsthawsthawythebethecatheedtheektheestheftthegntheictheintheirthelfthemathemethenstheowtherethermthesethespthetathetethewsthewythickthiefthighthigsthilkthillthinethingthinkthinsthiolthirdthirlthofttholetholithongthornthorothorpthosethousthowlthraethrawthreethrewthridthripthrobthroethrowthrumthudsthugsthujathumbthumpthunkthurlthuyathymethymithymytianstiaratiarstibiaticalticcaticedticestichytickstickytidaltiddytidedtidestierstiffstifostiftstigertigestighttigontikastikestikistikkatilaktildetiledtilertilestillstillytilthtiltstimbotimedtimertimestimidtimontimpstinastincttindstineatinedtinestingetingstinkstinnytintstintytipistippytipsytiredtirestirlstirostirrstitantitchtitertithetitistitletitretittytituptiyintiynstizestizzytoadstoadytoasttoazetockstockytocostodaytoddetoddytoeastoffstoffytoftstofustogaetogastogedtogestoguetohostoiletoilstoingtoisetoitstokaytokedtokentokertokestokostolantolartolastoledtolestollstollytoltstolustolyltomantombstomestomiatommytomostonaltonditondotonedtonertonestoneytongatongstonictonkatonkstonnetonustoolstoomstoonstoothtootstopaztopedtopeetopektopertopestophetophitophstopictopistopoitopostoppytoquetorahtorantorastorchtorcstorestorictoriitorostorottorrstorsetorsitorsktorsotortatortetortstorustosastosedtosestoshytossytotaltotedtotemtotertotestottytouchtoughtoukstounstourstousetousytoutstouzetouzytowedtoweltowertowietownstownytowsetowsytowtstowzetowzytoxictoxintoyedtoyertoyontoyostozedtozestozietrabstracetracktracttradetradstragitraiktrailtraintraittramptramstranktranqtranstranttrapetrapstrapttrashtrasstratstratttravetrawltrayftraystreadtreattrecktreedtreentreestrefatreiftrekstrematremstrendtresstresttretstrewstreyftreystriactriadtrialtribetricetricktridetriedtriertriestrifftrigotrigstriketrildtrilltrimstrinetrinstrioltriortriostripetripstripytristtritetroadtroaktroattrocktrodetrodstrogstroistroketrolltromptronatronctronetronktronstrooptrooztropetrothtrotstrouttrovetrowstroystrucetrucktruedtruertruestrugotrugstrulltrulytrumptrunktrusstrusttruthtryertryketrymatrypstrysttsadetsaditsarstskedtsubatsubotuanstuarttuathtubaetubaltubartubastubbytubedtubertubestuckstufastuffetuffstuftstuftytugratuiletuinatuismtuktutulestuliptulletulpatulsitumidtummytumortumpstumpytunastundstunedtunertunestungstunictunnytupektupiktupletuqueturboturdsturfsturfyturksturmeturmsturnsturntturpsturrstushytuskstuskytuteetutortuttituttytutustuxestuyertwaestwaintwalstwangtwanktwatstwaystweaktweedtweeltweentweeptweertweettwerktwerptwicetwiertwigstwilltwilttwinetwinktwinstwinytwiretwirltwirptwisttwitetwitstwixttwoertwyertyeestyerstyingtyiyntykestylertympstyndetynedtynestypaltypedtypestypeytypictypostyppstyptotyrantyredtyrestyrostythetzarsudalsudderudonsugaliuggeduhlanuhuruukaseulamaulansulcerulemaulminulnadulnaeulnarulnasulpanultraulvasulyieulzieumamiumbelumberumbleumbosumbraumbreumiacumiakumiaqummahummasummedumpedumphsumpieumptyumrahumrasunaisunaptunarmunaryunausunbagunbanunbarunbedunbidunboxuncapuncesunciauncleuncosuncoyuncusuncutundamundeeunderundidundosundueundugunethunfedunfitunfixungagungetungodungotungumunhatunhipunicaunifyunionuniteunitsunityunjamunkedunketunkidunlawunlayunledunletunlidunlitunmanunmetunmewunmixunpayunpegunpenunpinunredunridunrigunripunsawunsayunseeunsetunsewunsexunsoduntaxuntieuntiluntinunwedunwetunwitunwonunzipupbowupbyeupdosupdryupendupjetuplayupleduplituppedupperupranuprunupseeupsetupseyuptakupteruptieuraeiuraliuraosurareurariuraseurateurbanurbexurbiaurdeeurealureasuredoureicurenaurenturgedurgerurgesurialurineuriteurmanurnalurnedurpedursaeursidursonurubuurvasusageusersusherusingusneausqueusualusureusurpusuryuteriutileutteruvealuveasuvulavacuavadedvadesvagalvaguevagusvailsvairevairsvairyvakasvakilvalesvaletvalidvalisvalorvalsevaluevalvevampsvampyvandavanedvanesvangsvantsvapedvapervapesvapidvaporvaranvarasvardyvarecvaresvariavarixvarnavarusvarvevasalvasesvastsvastyvaticvatusvauchvaultvauntvautevautsvawtevaxesvealevealsvealyveenaveepsveersveeryveganvegasvegesvegievegosvehmeveilsveilyveinsveinyvelarveldsveldtvelesvellsvelumvenaevenalvendsvenduveneyvengeveninvenomventsvenuevenusverbsvergeverraverryverseversoverstvertsvertuvervevespavestavestsvetchvexedvexervexesvexilvezirvialsviandvibesvibexvibeyvicarvicedvicesvichyvideoviersviewsviewyvifdaviffsvigasvigiavigilvigorvildevilervillavillivillsvimenvinalvinasvincavinedvinervinesvinewvinicvinosvintsvinylviolavioldviolsviperviralviredvireoviresvirgavirgeviridvirlsvirtuvirusvisasvisedvisesvisievisitvisnevisonvisorvistavistovitaevitalvitasvitexvitrovittavivasvivatvivdavivervivesvividvixenvizirvizorvleisvliesvlogsvoarsvocabvocalvocesvoddyvodkavodouvodunvoemavogievoguevoicevoidsvoilavoilevoipsvolaevolarvoledvolesvoletvolksvoltavoltevoltivoltsvolvavolvevomervomitvotedvotervotesvouchvougevouluvowedvowelvowervoxelvozhdvraicvrilsvroomvrousvrouwvrowsvuggsvuggyvughsvughyvulgovulnsvulvavuttyvyingwaacswackewackowackswackywaddswaddywadedwaderwadeswadgewadiswadtswaferwaffswaftswagedwagerwageswaggawagonwagyuwahoowaidewaifswaiftwailswainswairswaistwaitewaitswaivewakaswakedwakenwakerwakeswakfswaldowaldswaledwalerwaleswaliewaliswalkswallawallswallywaltywaltzwamedwameswamuswandswanedwaneswaneywangswankswankywanlewanlywannawantswantywanzewaqfswarbswarbywardswaredwareswarezwarkswarmswarnswarpswarrewarstwartswartywaseswashywasmswaspswaspywastewastswatapwatchwaterwattswauffwaughwaukswaulkwaulswaurswavedwaverwaveswaveywawaswaweswawlswaxedwaxenwaxerwaxeswayedwazirwazoowealdwealsweambweanswearswearyweavewebbyweberwechtwedelwedgewedgyweedsweedyweekeweeksweelsweemsweensweenyweepsweepyweestweeteweetswefteweftsweidsweighweilsweirdweirsweiseweizewekaswelchweldswelkewelkswelktwellswellywelshweltswembswendswengewennywentsweroswershwestswetaswetlywexedwexeswhackwhalewhamowhamswhangwhapswharewharfwhatawhatswhaupwhaurwhealwhearwheatwheelwheenwheepwheftwhelkwhelmwhelpwhenswherewhetswhewswheyswhichwhidswhiffwhiftwhigswhilewhilkwhimswhinewhinswhinywhioswhipswhiptwhirlwhirrwhirswhishwhiskwhisswhistwhitewhitswhitywhizzwholewhompwhoofwhoopwhootwhopswhorewhorlwhortwhosewhosowhowswhumpwhupswhydawiccawickswickywiddywidenwiderwideswidowwidthwieldwielswifedwifeswifeywifiewiftywiganwiggawiggywightwikiswilcowildswiledwileswilgawiliswiljawillswillywiltswimpswimpywincewinchwindswindywinedwineswineywingewingswingywinkswinnawinnswinoswinzewipedwiperwipeswiredwirerwireswirrawisedwiserwiseswishawishtwispswispywistswitanwitchwitedwiteswithewithswithywittywivedwiverwiveswizenwizeswoadswoaldwockswodgewofulwojuswokenwokerwokkawoldswolfswollywolvewomanwombswombywomenwomynwongawongiwonkswonkywontswoodswoodywooedwooerwoofswoofywooldwoolswoolywoonswoopswoopywoosewooshwootzwoozywordswordyworksworldwormswormyworryworseworstworthwortswouldwoundwovenwowedwoweewoxenwrackwrangwrapswraptwrastwratewrathwrawlwreakwreckwrenswrestwrickwriedwrierwrieswringwristwritewritswrokewrongwrootwrotewrothwrungwryerwrylywuddywuduswullswurstwuseswushuwussywuxiawyledwyleswyndswynnswytedwytesxebecxeniaxenicxenonxericxeroxxerusxoanaxraysxylanxylemxylicxylolxylylxystixystsyaarsyabasyabbayabbyyaccayachtyackayacksyaffsyageryagesyagisyahooyairdyakkayakowyalesyamenyampyyamunyangsyanksyapokyaponyappsyappyyarakyarcoyardsyareryarfayarksyarnsyarrsyartayartoyatesyaudsyauldyaupsyawedyaweyyawlsyawnsyawnyyawpsyboreycladycledycondydradydredyeadsyeahsyealmyeansyeardyearnyearsyeastyecchyechsyechyyedesyeedsyeeshyeggsyelksyellsyelmsyelpsyeltsyentayenteyerbayerdsyerksyesesyesksyestsyestyyetisyettsyeuksyeukyyevenyevesyewenyexedyexesyfereyieldyikedyikesyillsyinceyipesyippyyirdsyirksyirrsyirthyitesyitieylemsylikeylkesymoltympesyobboyobbyyocksyodelyodhsyodleyogasyogeeyoghsyogicyoginyogisyoickyojanyokedyokelyokeryokesyokulyolksyolkyyomimyompsyonicyonisyonksyoofsyoopsyoresyorksyorpsyouksyoungyournyoursyourtyouseyouthyowedyowesyowieyowlsyowzayraptyrentyrivdyrnehysameytostyuansyucasyuccayucchyuckoyucksyuckyyuftsyugasyukedyukesyukkyyukosyulanyulesyummoyummyyumpsyuponyuppyyurtayurtsyuzuszabrazackszaidazaidyzairezakatzamanzambozamiazanjazantezanzazanzezappyzarfszariszatiszaxeszayinzazenzealszebeczebrazebubzebuszedaszeinszendozerdazerkszeroszestszestyzetaszexeszezeszhomozibetziffsziganzilaszilchzillazillszimbizimbszincozincszincyzinebzineszingszingyzinkezinkyzippozippyziramzitiszizelzizitzlotezlotyzoaeazoboszobuszoccozoeaezoealzoeaszoismzoistzombizonaezonalzondazonedzonerzoneszonkszooeazooeyzooidzookszoomszoonszootyzoppazoppozorilzoriszorrozoukszoweezowiezuluszupanzupaszuppazurfszuzimzygalzygonzymeszymic --------------------------------------------------------------------------------