From 0a34af5d0dc82e65aa4e11134a0458040554548f Mon Sep 17 00:00:00 2001 From: Terry Hearst Date: Thu, 7 Mar 2024 21:59:31 -0500 Subject: [PATCH] Add support for custom dates and words --- src/global.gd | 106 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/main.gd | 40 ++++++++++++------- src/menu.gd | 4 +- 3 files changed, 130 insertions(+), 20 deletions(-) diff --git a/src/global.gd b/src/global.gd index 91bfa42..629a75e 100644 --- a/src/global.gd +++ b/src/global.gd @@ -1,24 +1,34 @@ extends Node +enum GameMode { + DAILY, + RANDOM, + CUSTOM, +} + const MIN_LENGTH := 5 const MAX_LENGTH := 5 const GENERABLE_WORDS_FILENAME = "res://words/popular-filtered.txt" const ALL_WORDS_FILENAME := "res://words/enable1.txt" - -var daily_mode := true - +var game_mode := GameMode.DAILY +var custom_word := "" +var custom_date_str := "" var generable_words: Dictionary var all_words: Dictionary +var allow_future := false + func _ready() -> void: randomize() generable_words = parse_words(GENERABLE_WORDS_FILENAME, MIN_LENGTH, MAX_LENGTH) all_words = parse_words(ALL_WORDS_FILENAME, MIN_LENGTH, MAX_LENGTH) + detect_params() + func parse_words(filename: String, min_len: int, max_len: int) -> Dictionary: var dict := {} @@ -62,6 +72,94 @@ func is_valid_word(word: String) -> bool: return word.to_upper() in words_list +## Checks for command line/URL parameters and configures the game accordingly +func detect_params() -> void: + var daily := false + var custom := "" + + if OS.has_feature("web"): + var paramsStr = JavaScriptBridge.eval("window.location.search") + var paramsObj = JavaScriptBridge.create_object("URLSearchParams", paramsStr) + if paramsObj.has("daily") and paramsObj.get("daily") not in ["false", "0"]: + daily = true + if paramsObj.has("custom"): + custom = paramsObj.get("custom") + if paramsObj.has("allowfuture") and paramsObj.get("allowfuture") not in ["false", "0"]: + allow_future = true + else: + var args := OS.get_cmdline_user_args() + if "--daily" in args: + daily = true + var custom_index := args.find("--custom") + if custom_index != -1 and custom_index < (args.size() - 1): + custom = args[custom_index + 1] + if "--allowfuture" in args: + allow_future = true + + print("daily: ", daily) + print("custom: ", custom) + print("allowfuture: ", allow_future) + + if daily: + game_mode = GameMode.DAILY + get_tree().change_scene_to_file.call_deferred("res://src/main.tscn") + return + + if custom: + if parse_custom(custom): + game_mode = GameMode.CUSTOM + get_tree().change_scene_to_file.call_deferred("res://src/main.tscn") + return + + +## Checks if the given string is: +## +## * A normal five letter word, or +## * A valid encoded word, or +## * A valid date in YYYY-MM-DD encoding +## * Will only allow this date to be in the present or past, unless `allow_future` is set to true +## +## Returns true if successful, and will set `custom_word` and possibly `custom_date_str` values accordingly. Returns +## false if unsuccessful and will erase `custom_word` and `custom_date_str`. +func parse_custom(value: String) -> bool: + if value.length() == 5: + # ...is there an is_alpha function that I missed? + var word := value.to_upper() + var valid := true + for i in range(5): + var code := word.unicode_at(i) + if code < 65 or code > 90: # 65 = ascii "A", 90 = ascii "Z" + valid = false + break + if valid: + custom_word = word + return true + + if value.is_valid_hex_number(): + var decoded := decode_word(value.hex_to_int()) + if not decoded.is_empty(): + custom_word = decoded + return true + + var parsed_date := Time.get_datetime_dict_from_datetime_string(value, false) + if parsed_date.has("year"): + parsed_date["hour"] = 0 + parsed_date["minute"] = 0 + parsed_date["second"] = 0 + var new_time := Time.get_unix_time_from_datetime_dict(parsed_date) + var valid := true + if not allow_future: + var current_time := Time.get_unix_time_from_datetime_string(Time.get_date_string_from_system(true)) + if new_time > current_time: + valid = false + if valid: + custom_word = generate_word(5, new_time) + custom_date_str = Time.get_date_string_from_unix_time(new_time) + return true + + return false + + func _notification(what: int) -> void: if what == NOTIFICATION_WM_CLOSE_REQUEST: get_tree().quit() @@ -119,7 +217,7 @@ func decode_word(encoded_word: int) -> String: num -= 123456 - if num < 0 or num >= (26 ** 5): # (26 ** 5) - 1 = "zzzzz" + if num < 0 or num >= (26 ** 5): # (26 ** 5) - 1 = "ZZZZZ" # Someone is trying to be sneaky! Or more likely I just messed up. return "" diff --git a/src/main.gd b/src/main.gd index ecd0ee9..51377b6 100644 --- a/src/main.gd +++ b/src/main.gd @@ -46,27 +46,39 @@ func _ready() -> void: letter_grid.add_child(letter) letters.append(letter_array) - var random_seed = null - if Global.daily_mode: - var current_time := Time.get_datetime_dict_from_system(true) - current_time["hour"] = 0 - current_time["minute"] = 0 - current_time["second"] = 0 - random_seed = Time.get_unix_time_from_datetime_dict(current_time) - title.text = "Daily " + title.text - subtitle.text = "%d-%02d-%02d" % [current_time["year"], current_time["month"], current_time["day"]] + if Global.game_mode != Global.GameMode.CUSTOM: + Global.custom_word = "" + Global.custom_date_str = "" + + if not Global.custom_word.is_empty(): + target_word = Global.custom_word + title.text = "Custom " + title.text + + if Global.custom_date_str: + subtitle.text = Global.custom_date_str + else: + subtitle.text = String.num_int64(Global.encode_word(target_word), 16) else: - title.text = "Random " + title.text + var random_seed = null + if Global.game_mode == Global.GameMode.DAILY: + var current_time := Time.get_date_string_from_system(true) + random_seed = Time.get_unix_time_from_datetime_string(current_time) + + title.text = "Daily " + title.text + subtitle.text = current_time + elif Global.game_mode == Global.GameMode.RANDOM: + title.text = "Random " + title.text + + target_word = Global.generate_word(letter_count, random_seed) + + if Global.game_mode != Global.GameMode.DAILY: + subtitle.text = String.num_int64(Global.encode_word(target_word), 16) - target_word = Global.generate_word(letter_count, random_seed) current_guess = 0 ended = false won = false - if not Global.daily_mode: - subtitle.text = String.num_int64(Global.encode_word(target_word), 16) - for i in range(0, 26): var letter := char("A".unicode_at(0) + i) var keyboard_button: Button = keyboard_vbox.find_child("Button" + letter) diff --git a/src/menu.gd b/src/menu.gd index 02a5172..c61e6cd 100644 --- a/src/menu.gd +++ b/src/menu.gd @@ -11,13 +11,13 @@ func _ready() -> void: func _on_DailyButton_pressed() -> void: - Global.daily_mode = true + Global.game_mode = Global.GameMode.DAILY var error := get_tree().change_scene_to_file("res://src/main.tscn") assert(not error) func _on_RandomButton_pressed() -> void: - Global.daily_mode = false + Global.game_mode = Global.GameMode.RANDOM var error := get_tree().change_scene_to_file("res://src/main.tscn") assert(not error)