Change custom word format and allow different length words

This commit is contained in:
Terry Hearst 2024-03-19 21:39:11 -04:00
parent 66df0a9b44
commit cde8565f5d
3 changed files with 53 additions and 51 deletions

View file

@ -136,14 +136,14 @@ layout_mode = 2
[node name="WordLabel" type="Label" parent="C/V/Help/Options"]
layout_mode = 2
text = "A five letter word, e.g. \"APPLE\""
text = "A 3-8 letter word, e.g. \"APPLE\""
label_settings = SubResource("LabelSettings_6at68")
horizontal_alignment = 1
[node name="CodeLabel" type="Label" parent="C/V/Help/Options"]
layout_mode = 2
text = "An encoded word beginning with \"5$\",
e.g. \"5$BTEo\""
text = "An encoded word beginning with \"$\",
e.g. \"$bco92\""
label_settings = SubResource("LabelSettings_6at68")
horizontal_alignment = 1

View file

@ -7,8 +7,8 @@ enum GameMode {
CUSTOM,
}
const MIN_LENGTH := 5
const MAX_LENGTH := 5
const MIN_WORD_LENGTH := 3
const MAX_WORD_LENGTH := 8
const GENERABLE_WORDS_FILENAME = "res://words/popular-filtered.txt"
const ALL_WORDS_FILENAME := "res://words/enable1.txt"
@ -31,8 +31,8 @@ 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)
generable_words = parse_words(GENERABLE_WORDS_FILENAME, MIN_WORD_LENGTH, MAX_WORD_LENGTH)
all_words = parse_words(ALL_WORDS_FILENAME, MIN_WORD_LENGTH, MAX_WORD_LENGTH)
detect_params()
@ -71,7 +71,7 @@ func generate_word(length: int, random_seed = null) -> String:
func is_valid_word(word: String) -> bool:
var length := word.length()
if length < MIN_LENGTH or length > MAX_LENGTH:
if length < MIN_WORD_LENGTH or length > MAX_WORD_LENGTH:
return false
var words_list := all_words[length] as Array
@ -117,8 +117,8 @@ func detect_params() -> void:
## Checks if the given string is:
##
## * A normal five letter word, or
## * A valid encoded word, or
## * A normal alphabetical 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
##
@ -128,11 +128,18 @@ func parse_custom(value: String) -> bool:
custom_word = ""
custom_date_str = ""
if value.length() == 5:
var decoded := decode_word(value)
var decoded_len := decoded.length()
if decoded_len >= MIN_WORD_LENGTH and decoded_len <= MAX_WORD_LENGTH:
custom_word = decoded
return true
var value_len := value.length()
if value_len >= MIN_WORD_LENGTH and value_len <= MAX_WORD_LENGTH:
# ...is there an is_alpha function that I missed?
var word := value.to_upper()
var valid := true
for i in range(5):
for i in range(value_len):
var code := word.unicode_at(i)
if code < 65 or code > 90: # 65 = ascii "A", 90 = ascii "Z"
valid = false
@ -141,11 +148,6 @@ func parse_custom(value: String) -> bool:
custom_word = word
return true
var decoded := decode_word(value)
if not decoded.is_empty() and decoded.length() == 5:
custom_word = decoded
return true
if date_regex.search(value):
var parsed_date := Time.get_datetime_dict_from_datetime_string(value, false)
parsed_date["hour"] = 0
@ -187,49 +189,49 @@ func encode_word(word: String) -> String:
word = word.to_upper()
# Encode word in base 26
var num := 0
var num := 1 # Start with a single 1 bit
for i in range(word.length()):
var letter_ind := word[i].unicode_at(0) - 65 # 65 = ascii "A"
num *= 26
var letter_ind := word.unicode_at(i) - 65 # 65 = ascii "A"
if letter_ind < 0 or letter_ind >= 26:
return ""
num += letter_ind * (26 ** i)
# Add an offset so that "aaaaa" isn't just zeroes
num += 123456
num += letter_ind
return str(word.length()) + ENCODING_INDICATOR + large_encode(num)
# Bit count for redundancy
var count := popcnt(num)
num = (num << 6) + count
return ENCODING_INDICATOR + large_encode(num)
## Decodes an encoded word. Returns an empty string if invalid
func decode_word(encoded_word: String) -> String:
var index := encoded_word.find(ENCODING_INDICATOR)
if index < 1:
return ""
var len_str := encoded_word.substr(0, index)
if not len_str.is_valid_int():
return ""
var word_len := len_str.to_int()
if word_len < 1:
if not encoded_word.begins_with(ENCODING_INDICATOR):
return ""
var large_base_str := encoded_word.substr(index + 1)
var num := large_decode(large_base_str)
if num < 0:
var large_base_str := encoded_word.substr(1)
var full_num := large_decode(large_base_str)
# Check the bit count
var count := full_num & 0x3F # 6 bits
var num := full_num >> 6
if popcnt(num) != count:
return ""
num -= 123456
if num < 0 or num >= (26 ** word_len): # (26 ** 5) - 1 = "ZZZZZ"
# Someone is trying to be sneaky! Or more likely I just messed up.
if num <= 0:
return ""
# Decode base 26 word
var word := ""
for i in range(word_len):
while num > 1:
var letter_ind := num % 26
word += char(letter_ind + 65) # 65 = ascii "A"
word = char(letter_ind + 65) + word # 65 = ascii "A"
@warning_ignore("integer_division")
num = num / 26
if num != 1:
return ""
return word

View file

@ -1,7 +1,6 @@
extends Control
const letter_count := 5
const guess_count := 6
const Letter := preload("res://src/letter.tscn")
@ -13,6 +12,7 @@ const Letter := preload("res://src/letter.tscn")
# An array of arrays of the letter nodes
var letters := []
var keyboard_buttons := {}
var letter_count := 5
var target_word: String
var current_guess: int
@ -37,16 +37,6 @@ var input_guess: String
func _ready() -> void:
letter_grid.columns = letter_count
for _i in range(guess_count):
var letter_array := []
for _j in range(letter_count):
var letter := Letter.instantiate()
letter_array.append(letter)
letter_grid.add_child(letter)
letters.append(letter_array)
if Global.game_mode != Global.GameMode.CUSTOM:
Global.custom_word = ""
Global.custom_date_str = ""
@ -75,6 +65,16 @@ func _ready() -> void:
if Global.game_mode != Global.GameMode.DAILY:
subtitle.text = Global.encode_word(target_word)
letter_count = target_word.length()
letter_grid.columns = letter_count
for _i in range(guess_count):
var letter_array := []
for _j in range(letter_count):
var letter := Letter.instantiate()
letter_array.append(letter)
letter_grid.add_child(letter)
letters.append(letter_array)
current_guess = 0
ended = false
won = false
@ -132,7 +132,7 @@ func guess_entered() -> void:
return
input_guess = input_guess.to_upper()
if input_guess.length() != letter_count:
show_error("Word must be five characters.")
show_error("Word must be %d characters." % [letter_count])
return
if not Global.is_valid_word(input_guess) and input_guess != target_word:
show_error("Not a recognized word.")