Use more flexable encoding scheme
This commit is contained in:
parent
921e3351ef
commit
f76eca5b9c
2 changed files with 55 additions and 28 deletions
|
@ -142,14 +142,15 @@ horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="CodeLabel" type="Label" parent="C/V/Help/Options"]
|
[node name="CodeLabel" type="Label" parent="C/V/Help/Options"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "An encoded word, e.g. \"a00d57a\""
|
text = "An encoded word beginning with \"5$\",
|
||||||
|
e.g. \"a00d57a\""
|
||||||
label_settings = SubResource("LabelSettings_6at68")
|
label_settings = SubResource("LabelSettings_6at68")
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="DateLabel" type="Label" parent="C/V/Help/Options"]
|
[node name="DateLabel" type="Label" parent="C/V/Help/Options"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "A date in YYYY-MM-DD format, e.g.
|
text = "A date in YYYY-MM-DD format,
|
||||||
\"2024-01-23\""
|
e.g. \"2024-01-23\""
|
||||||
label_settings = SubResource("LabelSettings_6at68")
|
label_settings = SubResource("LabelSettings_6at68")
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,11 @@ const MAX_LENGTH := 5
|
||||||
const GENERABLE_WORDS_FILENAME = "res://words/popular-filtered.txt"
|
const GENERABLE_WORDS_FILENAME = "res://words/popular-filtered.txt"
|
||||||
const ALL_WORDS_FILENAME := "res://words/enable1.txt"
|
const ALL_WORDS_FILENAME := "res://words/enable1.txt"
|
||||||
|
|
||||||
|
# !$'()*+,-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz <- All URL-encodable characters
|
||||||
|
const LARGE_BASE_CHARS := "!'()*+,-.123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz" # <- Without some similar looking characters
|
||||||
|
const LARGE_BASE := 68 # Number of characters in above array
|
||||||
|
const ENCODING_INDICATOR := "$"
|
||||||
|
|
||||||
var date_regex = RegEx.create_from_string("^[0-9]+-[0-9]+-[0-9]+$")
|
var date_regex = RegEx.create_from_string("^[0-9]+-[0-9]+-[0-9]+$")
|
||||||
|
|
||||||
var game_mode := GameMode.DAILY
|
var game_mode := GameMode.DAILY
|
||||||
|
@ -137,7 +142,7 @@ func parse_custom(value: String) -> bool:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
var decoded := decode_word(value)
|
var decoded := decode_word(value)
|
||||||
if not decoded.is_empty():
|
if not decoded.is_empty() and decoded.length() == 5:
|
||||||
custom_word = decoded
|
custom_word = decoded
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
@ -179,8 +184,6 @@ func popcnt(num: int) -> int:
|
||||||
|
|
||||||
## Encodes a word using a very basic "encryption" method. Returns an empty string if invalid word
|
## Encodes a word using a very basic "encryption" method. Returns an empty string if invalid word
|
||||||
func encode_word(word: String) -> String:
|
func encode_word(word: String) -> String:
|
||||||
if word.length() != 5:
|
|
||||||
return ""
|
|
||||||
word = word.to_upper()
|
word = word.to_upper()
|
||||||
|
|
||||||
# Encode word in base 26
|
# Encode word in base 26
|
||||||
|
@ -193,46 +196,69 @@ func encode_word(word: String) -> String:
|
||||||
# Add an offset so that "aaaaa" isn't just zeroes
|
# Add an offset so that "aaaaa" isn't just zeroes
|
||||||
num += 123456
|
num += 123456
|
||||||
|
|
||||||
# Shuffle bits around
|
return str(word.length()) + ENCODING_INDICATOR + large_encode(num)
|
||||||
# uvwxyz -> vuxwzy -> zyvuxw
|
|
||||||
num = ((num & 0xf0f0f0) >> 4) | ((num & 0x0f0f0f) << 4)
|
|
||||||
num = ((num & 0xffff00) >> 8) | ((num & 0x0000ff) << 16)
|
|
||||||
# Count bits and add as checksum
|
|
||||||
var bits := popcnt(num)
|
|
||||||
num = num | (bits << 24)
|
|
||||||
|
|
||||||
return String.num_int64(num, 16)
|
|
||||||
|
|
||||||
|
|
||||||
## Decodes an encoded word. Returns an empty string if invalid
|
## Decodes an encoded word. Returns an empty string if invalid
|
||||||
func decode_word(encoded_word: String) -> String:
|
func decode_word(encoded_word: String) -> String:
|
||||||
if not encoded_word.is_valid_hex_number():
|
var index := encoded_word.find(ENCODING_INDICATOR)
|
||||||
|
if index < 1:
|
||||||
return ""
|
return ""
|
||||||
var encoded_word_num := encoded_word.hex_to_int()
|
var len_str := encoded_word.substr(0, index)
|
||||||
|
if not len_str.is_valid_int():
|
||||||
var bits := encoded_word_num >> 24
|
return ""
|
||||||
var num := encoded_word_num & 0xffffff
|
var word_len := len_str.to_int()
|
||||||
|
if word_len < 1:
|
||||||
# Verify bit count
|
|
||||||
if popcnt(num) != bits:
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
# Unshuffle bits
|
var large_base_str := encoded_word.substr(index + 1)
|
||||||
num = ((num << 8) & 0xffff00) | ((num >> 16) & 0x0000ff)
|
var num := large_decode(large_base_str)
|
||||||
num = ((num << 4) & 0xf0f0f0) | ((num >> 4) & 0x0f0f0f)
|
if num < 0:
|
||||||
|
return ""
|
||||||
|
|
||||||
num -= 123456
|
num -= 123456
|
||||||
|
|
||||||
if num < 0 or num >= (26 ** 5): # (26 ** 5) - 1 = "ZZZZZ"
|
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.
|
# Someone is trying to be sneaky! Or more likely I just messed up.
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
# Decode base 26 word
|
# Decode base 26 word
|
||||||
var word := ""
|
var word := ""
|
||||||
for i in range(5):
|
for i in range(word_len):
|
||||||
var letter_ind := num % 26
|
var letter_ind := num % 26
|
||||||
word += char(letter_ind + 65) # 65 = ascii "A"
|
word += char(letter_ind + 65) # 65 = ascii "A"
|
||||||
@warning_ignore("integer_division")
|
@warning_ignore("integer_division")
|
||||||
num = num / 26
|
num = num / 26
|
||||||
|
|
||||||
return word
|
return word
|
||||||
|
|
||||||
|
|
||||||
|
## Encodes an integer as a large-base string
|
||||||
|
func large_encode(num: int) -> String:
|
||||||
|
if num <= 0: # Negative numbers not supported since they are not needed
|
||||||
|
return LARGE_BASE_CHARS[0]
|
||||||
|
var lint := ""
|
||||||
|
while num > 0:
|
||||||
|
lint = LARGE_BASE_CHARS[num % LARGE_BASE] + lint
|
||||||
|
@warning_ignore("integer_division")
|
||||||
|
num = num / LARGE_BASE
|
||||||
|
return lint
|
||||||
|
|
||||||
|
|
||||||
|
## Checks if the given string is a valid large-base integer. (Sorta, it only checks if the character composition is good)
|
||||||
|
func is_valid_large_base_int(lint: String) -> bool:
|
||||||
|
for chr in lint:
|
||||||
|
if chr not in LARGE_BASE_CHARS:
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
## Converts the given large-base integer back into a normal int. Returns -1 if invalid
|
||||||
|
func large_decode(lint: String) -> int:
|
||||||
|
var num := 0
|
||||||
|
for chr in lint:
|
||||||
|
var index := LARGE_BASE_CHARS.find(chr)
|
||||||
|
if index < 0:
|
||||||
|
return -1
|
||||||
|
num = (num * LARGE_BASE) + index
|
||||||
|
return num
|
||||||
|
|
Loading…
Reference in a new issue